/* * (C) Copyright 1992, ..., 2005 the "DOSEMU-Development-Team". * * for details see file COPYING in the DOSEMU distribution */ #define SDL_C #include "config.h" #include #include /* for malloc & free */ #include /* for memset */ #include #include #include #include #include #include "emu.h" #include "init.h" #include "bios.h" #include "video.h" #include "memory.h" #include "../../env/video/remap.h" #include "../X/screen.h" #include "vgaemu.h" #include "vgatext.h" #include "render.h" #include "sdl.h" #include "keyb_clients.h" #include "dos2linux.h" #include "utilities.h" #include "speaker.h" static int SDL_init(void); static void SDL_close(void); static int SDL_set_videomode(int mode_class, int text_width, int text_height); static void SDL_update_cursor(void); static int SDL_update_screen(void); static void SDL_refresh_private_palette(void); static void SDL_put_image(int x, int y, unsigned width, unsigned height); static void SDL_change_mode(int *x_res, int *y_res); static void SDL_resize_image(unsigned width, unsigned height); static void SDL_handle_events(void); static int SDL_set_text_mode(int tw, int th, int w ,int h); /* interface to xmode.exe */ static int SDL_change_config(unsigned, void *); static void toggle_grab(void); struct video_system Video_SDL = { 0, SDL_init, SDL_close, SDL_set_videomode, SDL_update_screen, SDL_update_cursor, SDL_change_config, SDL_handle_events }; struct render_system Render_SDL = { SDL_refresh_private_palette, SDL_put_image, }; static const SDL_VideoInfo *video_info; #ifdef X_SUPPORT #ifdef CONFIG_SELECTION #ifdef USE_DL_PLUGINS #define X_handle_selection pX_handle_selection static void (*X_handle_selection)(Display *display, Window mainwindow, XEvent *e); #endif #define CONFIG_SDL_SELECTION 1 #endif #endif static int using_x11 = 0; static int remap_src_modes = 0; static SDL_Surface* surface = NULL; static int SDL_image_mode; static Boolean is_mapped = FALSE; static int exposure = 0; static int font_width = 8, font_height = 16, font_shift = 1; static int w_x_res, w_y_res; /* actual window size */ static int saved_w_x_res, saved_w_y_res; /* saved normal window size */ /* For graphics mode */ static vga_emu_update_type veut; static ColorSpaceDesc SDL_csd; static SDL_Color vga_colors[256]; static struct { int num, max; SDL_Rect *rects; } sdl_rects; static int force_grab = 0; int grab_active = 0; int SDL_init(void) { SDL_Event evt; char driver[8]; int have_true_color; use_bitmap_font = 1; if (SDL_Init(SDL_INIT_VIDEO| SDL_INIT_NOPARACHUTE) < 0) { error("SDL: %s\n", SDL_GetError()); leavedos(99); } SDL_EnableUNICODE(1); SDL_VideoDriverName(driver, 8); v_printf("SDL: Using driver: %s\n", driver); /* Collect some data */ video_info = SDL_GetVideoInfo(); if (video_info->wm_available) SDL_WM_SetCaption(config.X_title, config.X_icon_name); else config.X_fullscreen = 1; if (config.X_fullscreen) toggle_grab(); SDL_csd.bits = video_info->vfmt->BitsPerPixel; SDL_csd.bytes = (video_info->vfmt->BitsPerPixel + 7) >> 3; SDL_csd.r_mask = video_info->vfmt->Rmask; SDL_csd.g_mask = video_info->vfmt->Gmask; SDL_csd.b_mask = video_info->vfmt->Bmask; color_space_complete(&SDL_csd); set_remap_debug_msg(stderr); have_true_color = (video_info->vfmt->palette == NULL); remap_src_modes = remapper_init(&SDL_image_mode, SDL_csd.bits, have_true_color, 0); if(have_true_color) Render_SDL.refresh_private_palette = NULL; register_render_system(&Render_SDL); if(vga_emu_init(remap_src_modes, &SDL_csd)) { error("SDL: SDL_init: VGAEmu init failed!\n"); leavedos(99); } #ifdef X_SUPPORT { SDL_SysWMinfo info; SDL_VERSION(&info.version); if (SDL_GetWMInfo(&info) && info.subsystem == SDL_SYSWM_X11) { #ifdef USE_DL_PLUGINS void (*X_speaker_on)(void *, unsigned, unsigned short); void (*X_speaker_off)(void *); void *handle = load_plugin("X"); X_speaker_on = dlsym(handle, "X_speaker_on"); X_speaker_off = dlsym(handle, "X_speaker_off"); #ifdef CONFIG_SDL_SELECTION X_handle_selection = dlsym(handle, "X_handle_selection"); #endif #endif SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); register_speaker(info.info.x11.display, X_speaker_on, X_speaker_off); using_x11 = 1; } } #endif /* SDL_APPACTIVE event does not occur when an application window is first * created. * So we push that event into the queue */ evt.type = SDL_ACTIVEEVENT; evt.active.type = SDL_ACTIVEEVENT; evt.active.gain = 1; evt.active.state = SDL_APPACTIVE; SDL_PushEvent(&evt); /* Same for focus event */ evt.type = SDL_ACTIVEEVENT; evt.active.type = SDL_ACTIVEEVENT; evt.active.gain = 1; evt.active.state = SDL_APPINPUTFOCUS; SDL_PushEvent(&evt); /* enable repeat here (in the keyboard code it's too early) */ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); return 0; } void SDL_close(void) { remapper_done(); vga_emu_done(); SDL_Quit(); } /* NOTE : Like X.c, the actual mode is taken via video_mode */ int SDL_set_videomode(int mode_class, int text_width, int text_height) { int mode = video_mode; if(mode_class != -1) { if(!vga_emu_setmode(mode, text_width, text_height)) { v_printf("vga_emu_setmode(%d, %d, %d) failed\n", mode, text_width, text_height); return 0; } font_width = vga.char_width; font_height = vga.char_height; } v_printf("X: X_setmode: %svideo_mode 0x%x (%s), size %d x %d (%d x %d pixel)\n", mode_class != -1 ? "" : "re-init ", (int) mode, vga.mode_class ? "GRAPH" : "TEXT", vga.text_width, vga.text_height, vga.width, vga.height ); if (surface != NULL) { SDL_FreeSurface(surface); } if(vga.mode_class == TEXT) { SDL_set_text_mode(vga.text_width, vga.text_height, vga.width, vga.height); if (!grab_active) SDL_ShowCursor(SDL_ENABLE); if (is_mapped) reset_redraw_text_screen(); } else { get_mode_parameters(&w_x_res, &w_y_res, SDL_image_mode, &veut); SDL_change_mode(&w_x_res, &w_y_res); } return 1; } void SDL_resize_image(unsigned width, unsigned height) { v_printf("SDL: resize_image %d x %d\n", width, height); w_x_res = width; w_y_res = height; SDL_change_mode(&w_x_res, &w_y_res); } static void SDL_redraw_resize_image(unsigned width, unsigned height) { SDL_resize_image(width, height); dirty_all_video_pages(); if (vga.mode_class == TEXT) vga.reconfig.mem = 1; SDL_update_screen(); } int SDL_set_text_mode(int tw, int th, int w ,int h) { /* Try to find the best font */ /* We only have fonts by 8 x y pixels, so we just have to choose y */ font_width = vga.char_width; font_shift = (w / tw ) - vga.char_width; /* re adjust h */ h = vga.char_height * th; resize_text_mapper(SDL_image_mode); SDL_resize_image(w, h); SDL_set_mouse_text_cursor(); /* that's all */ return 0; } static void SDL_change_mode(int *x_res, int *y_res) { Uint32 flags = SDL_HWPALETTE | SDL_HWSURFACE; saved_w_x_res = *x_res; saved_w_y_res = *y_res; if (config.X_fullscreen) { SDL_Rect **modes; int i; modes=SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE); if (modes == (SDL_Rect **) 0) { modes=SDL_ListModes(NULL, SDL_FULLSCREEN); } if (modes != (SDL_Rect **) -1) { unsigned mw = 0; do { unsigned mh = 0; int factor; mw++; for (i = 0; modes[i] && modes[i]->w >= mw*vga.width; i++); if (i > 0) i--; do { mh++; while (modes[i]->h < mh*vga.height && i > 0) i--; if (modes[i]) { factor = modes[i]->h / vga.height; *y_res = vga.height * factor; } } while (modes[i]->h - *y_res > *y_res/2); /* while the border is too high */ factor = modes[i]->w / vga.width; *x_res = vga.width * factor; } while (modes[i]->w - *x_res > *x_res/2); /* while the border is too wide */ v_printf("SDL: using fullscreen mode: x=%d, y=%d\n", modes[i]->w, modes[i]->h); } flags |= SDL_FULLSCREEN; } else { flags |= SDL_RESIZABLE; } v_printf("SDL: using mode %d %d %d\n", *x_res, *y_res, SDL_csd.bits); if (!using_x11) /* SDL may crash otherwise.. */ SDL_ShowCursor(SDL_ENABLE); surface = SDL_SetVideoMode(*x_res, *y_res, SDL_csd.bits, flags); SDL_ShowCursor(SDL_DISABLE); remap_obj.dst_resize(&remap_obj, *x_res, *y_res, surface->pitch); remap_obj.dst_image = surface->pixels; *remap_obj.dst_color_space = SDL_csd; } static void SDL_update(void) { SDL_UpdateRects(surface, sdl_rects.num, sdl_rects.rects); sdl_rects.num = 0; } void SDL_update_cursor(void) { /* no hardware cursor emulation in graphics modes (erik@sjoerd) */ if(vga.mode_class == GRAPH) return; else if (is_mapped) { SDL_LockSurface(surface); update_cursor(); SDL_UnlockSurface(surface); SDL_update(); } } int SDL_update_screen(void) { if(vga.reconfig.re_init) { vga.reconfig.re_init = 0; dirty_all_video_pages(); dirty_all_vga_colors(); SDL_set_videomode(-1, 0, 0); } if (is_mapped) { int ret; SDL_LockSurface(surface); ret = update_screen(&veut); SDL_UnlockSurface(surface); SDL_update(); return ret; } return 0; } static void SDL_refresh_private_palette(void) { /* SDL cannot change colors 1, 5, 7... in a single step, cause * the SetColors command can only change consecutive colors. * * So there were three choices : * i. Do a SetColor for each color that changed * ii. Do a SetColor for the whole palette. This need the current palette * to be saved in memory * iii. Group all changed consecutive colors and do a SetColor for each group * The 2nd solution is faster (5/10 times) than the first, and is implemented * here. The 3rd solution is perhaps better but i haven't really tried * to implement it */ DAC_entry col[256]; int cols; RGBColor c; int i, j, k; unsigned bits, shift; cols = 1 << vga.pixel_size; if(cols > 256) cols = 256; j = changed_vga_colors(col); for(i = k = 0; k < j; k++) { c.r = col[k].r; c.g = col[k].g; c.b = col[k].b; bits = vga.dac.bits; gamma_correct(&remap_obj, &c, &bits); if (bits < 8) shift = 8 - bits; else shift = 0; vga_colors[col[k].index].r = c.r << shift; vga_colors[col[k].index].g = c.g << shift; vga_colors[col[k].index].b = c.b << shift; } if (j) SDL_SetColors(surface, vga_colors, 0, cols); } /* this only pushes the rectangle on a stack; updating is done later */ void SDL_put_image(int x, int y, unsigned width, unsigned height) { SDL_Rect *rect; if (sdl_rects.num >= sdl_rects.max) { sdl_rects.rects = realloc(sdl_rects.rects, (sdl_rects.max + 10) * sizeof(*sdl_rects.rects)); sdl_rects.max += 10; } rect = &sdl_rects.rects[sdl_rects.num]; rect->x = x; rect->y = y; rect->w = width; rect->h = height; sdl_rects.num++; } static void toggle_grab(void) { if(grab_active ^= 1) { v_printf("SDL: grab activated\n"); if (!config.X_fullscreen) SDL_WM_GrabInput(SDL_GRAB_ON); config.mouse.use_absolute = 0; v_printf("SDL: mouse grab activated\n"); SDL_ShowCursor(SDL_DISABLE); mouse_enable_native_cursor(1); } else { config.mouse.use_absolute = 1; v_printf("SDL: grab released\n"); if (!config.X_fullscreen) SDL_WM_GrabInput(SDL_GRAB_OFF); if(vga.mode_class == TEXT) SDL_ShowCursor(SDL_ENABLE); mouse_enable_native_cursor(0); } SDL_change_config(CHG_TITLE, NULL); } static void toggle_fullscreen_mode(void) { config.X_fullscreen = !config.X_fullscreen; if (config.X_fullscreen) { v_printf("SDL: entering fullscreen mode\n"); if (!grab_active) { toggle_grab(); force_grab = 1; } SDL_redraw_resize_image(w_x_res, w_y_res); } else { v_printf("SDL: entering windowed mode!\n"); SDL_redraw_resize_image(saved_w_x_res, saved_w_y_res); if (force_grab && grab_active) { toggle_grab(); } force_grab = 0; } } /* * This function provides an interface to reconfigure parts * of SDL and the VGA emulation during a DOSEMU session. * It is used by the xmode.exe program that comes with DOSEMU. */ static int SDL_change_config(unsigned item, void *buf) { int err = 0; v_printf("SDL: SDL_change_config: item = %d, buffer = %p\n", item, buf); switch(item) { case CHG_TITLE: /* low-level write */ if (buf) { v_printf("SDL: SDL_change_config: win_name = %s\n", (char *) buf); SDL_WM_SetCaption(buf, config.X_icon_name); break; } /* high-level write (shows name of emulator + running app) */ /* fallthrough */ case CHG_TITLE_EMUNAME: case CHG_TITLE_APPNAME: case CHG_TITLE_SHOW_APPNAME: case CHG_WINSIZE: case CHG_BACKGROUND_PAUSE: case GET_TITLE_APPNAME: change_config (item, buf, grab_active, grab_active); break; case CHG_FULLSCREEN: v_printf("SDL: SDL_change_config: fullscreen %i\n", *((int *) buf)); if (*((int *) buf) == !config.X_fullscreen) toggle_fullscreen_mode(); break; default: err = 100; } return err; } #if CONFIG_SDL_SELECTION /* * Convert X coordinate to column, with bounds checking. */ static int x_to_col(int x) { int col = vga.width*x/font_width/w_x_res; if (col < 0) col = 0; else if (col >= vga.text_width) col = vga.text_width-1; return(col); } /* * Convert Y coordinate to row, with bounds checking. */ static int y_to_row(int y) { int row = vga.height*y/font_height/w_y_res; if (row < 0) row = 0; else if (row >= vga.text_height) row = vga.text_height-1; return(row); } static void SDL_handle_selection(XEvent *e) { SDL_SysWMinfo info; switch(e->type) { case SelectionClear: case SelectionNotify: case SelectionRequest: case ButtonRelease: SDL_VERSION(&info.version); if (SDL_GetWMInfo(&info) && info.subsystem == SDL_SYSWM_X11) { info.info.x11.lock_func(); X_handle_selection(info.info.x11.display, info.info.x11.window, e); info.info.x11.unlock_func(); } break; default: break; } } #endif /* CONFIG_SDL_SELECTION */ static void SDL_handle_events(void) { static int busy = 0; SDL_Event event; if (busy) return; busy = 1; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_ACTIVEEVENT: if (event.active.state == SDL_APPACTIVE) { if (event.active.gain == 1) { v_printf("Expose Event\n"); is_mapped = TRUE; if (vga.mode_class == TEXT) { if (!exposure) { redraw_text_screen(); exposure = 1; } } else { /* TODO */ } } else { /* TODO */ } } else if (event.active.state == SDL_APPINPUTFOCUS || SDL_APPMOUSEFOCUS) { if (event.active.gain == 1) { v_printf("SDL: focus in\n"); if (vga.mode_class == TEXT) text_gain_focus(); } else { v_printf("SDL: focus out\n"); if (vga.mode_class == TEXT) text_lose_focus(); } } break; case SDL_VIDEORESIZE: SDL_redraw_resize_image(event.resize.w, event.resize.h); break; case SDL_KEYDOWN: { SDL_keysym keysym = event.key.keysym; if ((keysym.mod & KMOD_CTRL) && (keysym.mod & KMOD_ALT)) { if (keysym.sym == SDLK_HOME || keysym.sym == SDLK_k) { force_grab = 0; toggle_grab(); break; } else if (keysym.sym == SDLK_f) { toggle_fullscreen_mode(); break; } } } #if CONFIG_SDL_SELECTION clear_if_in_selection(); #endif SDL_process_key(event.key); break; case SDL_KEYUP: #if CONFIG_SDL_SELECTION clear_if_in_selection(); #endif SDL_process_key(event.key); break; case SDL_MOUSEBUTTONDOWN: { int buttons = SDL_GetMouseState(NULL, NULL); #if CONFIG_SDL_SELECTION if (using_x11 && vga.mode_class == TEXT && !grab_active) { if (event.button.button == SDL_BUTTON_LEFT) start_selection(x_to_col(event.button.x), y_to_row(event.button.y)); else if (event.button.button == SDL_BUTTON_RIGHT) start_extend_selection(x_to_col(event.button.x), y_to_row(event.button.y)); } #endif /* CONFIG_SDL_SELECTION */ SDL_set_mouse_move(event.button.x, event.button.y, w_x_res, w_y_res); mouse_move_buttons(buttons & SDL_BUTTON(1), buttons & SDL_BUTTON(2), buttons & SDL_BUTTON(3)); break; } case SDL_MOUSEBUTTONUP: { int buttons = SDL_GetMouseState(NULL, NULL); SDL_set_mouse_move(event.button.x, event.button.y, w_x_res, w_y_res); #if CONFIG_SDL_SELECTION if (using_x11 && vga.mode_class == TEXT) { XEvent e; e.type = ButtonRelease; e.xbutton.button = 0; if (event.button.button == SDL_BUTTON_LEFT) e.xbutton.button = Button1; else if (event.button.button == SDL_BUTTON_MIDDLE) e.xbutton.button = Button2; else if (event.button.button == SDL_BUTTON_RIGHT) e.xbutton.button = Button3; e.xbutton.time = CurrentTime; SDL_handle_selection(&e); } #endif /* CONFIG_SDL_SELECTION */ mouse_move_buttons(buttons & SDL_BUTTON(1), buttons & SDL_BUTTON(2), buttons & SDL_BUTTON(3)); break; } case SDL_MOUSEMOTION: #if CONFIG_SDL_SELECTION if (using_x11) extend_selection(x_to_col(event.button.x), y_to_row(event.button.y)); #endif /* CONFIG_SDL_SELECTION */ SDL_set_mouse_move(event.button.x, event.button.y, w_x_res, w_y_res); break; case SDL_QUIT: leavedos(0); break; #if CONFIG_SDL_SELECTION case SDL_SYSWMEVENT: if (using_x11) SDL_handle_selection(&event.syswm.msg->event.xevent); break; #endif /* CONFIG_SDL_SELECTION */ default: v_printf("PAS ENCORE TRAITE\n"); /* TODO */ break; } } busy = 0; do_mouse_irq(); } CONSTRUCTOR(static void init(void)) { if (Video) return; config.X = 1; Video = &Video_SDL; register_keyboard_client(&Keyboard_SDL); register_mouse_client(&Mouse_SDL); }