///////////////////////////////////////////////////////////////////////// // $Id: win32.cc,v 1.143 2011-01-25 15:49:32 sshwarts Exp $ ///////////////////////////////////////////////////////////////////////// // // Copyright (C) 2002-2009 The Bochs Project // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ///////////////////////////////////////////////////////////////////////// // Much of this file was written by: // David Ross // dross@pobox.com // Define BX_PLUGGABLE in files that can be compiled into plugins. For // platforms that require a special tag on exported symbols, BX_PLUGGABLE // is used to know when we are exporting symbols and when we are importing. #define BX_PLUGGABLE #include "bochs.h" #include "param_names.h" #include "keymap.h" #include "iodev/iodev.h" #include "iodev/vga.h" #if BX_WITH_WIN32 #include "zmouse.h" #include "win32dialog.h" #include "win32res.h" #include "font/vga.bitmap.h" // windows.h is included by bochs.h #include #include class bx_win32_gui_c : public bx_gui_c { public: bx_win32_gui_c (void) {} DECLARE_GUI_VIRTUAL_METHODS(); virtual void statusbar_setitem(int element, bx_bool active, bx_bool w=0); virtual void get_capabilities(Bit16u *xres, Bit16u *yres, Bit16u *bpp); virtual void set_tooltip(unsigned hbar_id, const char *tip); #if BX_SHOW_IPS virtual void show_ips(Bit32u ips_count); #endif }; // declare one instance of the gui object and call macro to insert the // plugin code static bx_win32_gui_c *theGui = NULL; IMPLEMENT_GUI_PLUGIN_CODE(win32) #define LOG_THIS theGui-> #define EXIT_GUI_SHUTDOWN 1 #define EXIT_GMH_FAILURE 2 #define EXIT_FONT_BITMAP_ERROR 3 #define EXIT_NORMAL 4 #define EXIT_HEADER_BITMAP_ERROR 5 #ifndef TBSTYLE_FLAT #define TBSTYLE_FLAT 0x0800 #endif /* FIXME: Should we add a bochsrc option to control the font usage? */ #define BX_USE_WINDOWS_FONTS 0 // Keyboard/mouse stuff #define SCANCODE_BUFSIZE 20 #define MOUSE_PRESSED 0x20000000 #define HEADERBAR_CLICKED 0x08000000 #define MOUSE_MOTION 0x22000000 #define BX_SYSKEY (KF_UP|KF_REPEAT|KF_ALTDOWN) void enq_key_event(Bit32u, Bit32u); void enq_mouse_event(void); struct QueueEvent { Bit32u key_event; int mouse_x; int mouse_y; int mouse_z; int mouse_button_state; }; QueueEvent* deq_key_event(void); static QueueEvent keyevents[SCANCODE_BUFSIZE]; static unsigned head=0, tail=0; static int mouse_button_state = 0; static int ms_xdelta=0, ms_ydelta=0, ms_zdelta=0; static int ms_lastx=0, ms_lasty=0; static int ms_savedx=0, ms_savedy=0; static BOOL mouseCaptureMode, mouseCaptureNew, mouseToggleReq; static UINT_PTR workerThread = 0; static DWORD workerThreadID = 0; static int mouse_buttons = 3; // Graphics screen stuff static unsigned x_tilesize = 0, y_tilesize = 0; static BITMAPINFO* bitmap_info=(BITMAPINFO*)0; static RGBQUAD* cmap_index; // indeces into system colormap static HBITMAP MemoryBitmap = NULL; static HDC MemoryDC = NULL; static RECT updated_area; static BOOL updated_area_valid = FALSE; static HWND desktopWindow; static RECT desktop; static BOOL queryFullScreen = FALSE; static int desktop_x, desktop_y; static BOOL toolbarVisible, statusVisible; // Text mode screen stuff static unsigned prev_cursor_x = 0; static unsigned prev_cursor_y = 0; static HBITMAP vgafont[256]; static int xChar = 8, yChar = 16; static unsigned int text_rows=25, text_cols=80; static Bit8u text_pal_idx[16]; #if !BX_USE_WINDOWS_FONTS static Bit8u h_panning = 0, v_panning = 0; static Bit16u line_compare = 1023; #else static HFONT hFont[3]; static int FontId = 2; #endif // Headerbar stuff HWND hwndTB, hwndSB; static unsigned bx_bitmap_entries; static struct { HBITMAP bmap; unsigned xdim; unsigned ydim; } bx_bitmaps[BX_MAX_PIXMAPS]; static struct { unsigned bmap_id; void (*f)(void); const char *tooltip; } bx_headerbar_entry[BX_MAX_HEADERBAR_ENTRIES]; static int bx_headerbar_entries; static unsigned bx_hb_separator; // Status Bar stuff #if BX_SHOW_IPS static BOOL ipsUpdate = FALSE; static char ipsText[20]; #define BX_SB_TEXT_ELEMENTS 2 #else #define BX_SB_TEXT_ELEMENTS 1 #endif #define SIZE_OF_SB_ELEMENT 40 #define SIZE_OF_SB_MOUSE_MESSAGE 170 #define SIZE_OF_SB_IPS_MESSAGE 90 long SB_Edges[BX_MAX_STATUSITEMS+BX_SB_TEXT_ELEMENTS+1]; char SB_Text[BX_MAX_STATUSITEMS][10]; bx_bool SB_Active[BX_MAX_STATUSITEMS]; bx_bool SB_ActiveW[BX_MAX_STATUSITEMS]; // Misc stuff static unsigned dimension_x, dimension_y, current_bpp; static unsigned stretched_x, stretched_y; static unsigned stretch_factor=1; static BOOL BxTextMode = TRUE; static BOOL fix_size = FALSE; #if BX_DEBUGGER && BX_DEBUGGER_GUI static BOOL gui_debug = FALSE; #endif static HWND hotKeyReceiver = NULL; static HWND saveParent = NULL; static char szMouseEnable[40]; static char szMouseDisable[40]; static char szMouseTooltip[64]; static const char szAppName[] = "Bochs for Windows"; static const char szWindowName[] = "Bochs for Windows - Display"; typedef struct { HINSTANCE hInstance; CRITICAL_SECTION drawCS; CRITICAL_SECTION keyCS; CRITICAL_SECTION mouseCS; int kill; // reason for terminateEmul(int) BOOL UIinited; HWND mainWnd; HWND simWnd; } sharedThreadInfo; sharedThreadInfo stInfo; LRESULT CALLBACK mainWndProc (HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK simWndProc (HWND, UINT, WPARAM, LPARAM); VOID CDECL UIThread(PVOID); void SetStatusText(int Num, const char *Text, bx_bool active, bx_bool w=0); void terminateEmul(int); void create_vga_font(void); static unsigned char reverse_bitorder(unsigned char); void DrawBitmap (HDC, HBITMAP, int, int, int, int, int, int, DWORD, unsigned char); void DrawChar (HDC, unsigned char, int, int, unsigned char cColor, int, int); void updateUpdated(int,int,int,int); static void headerbar_click(int x); #if BX_USE_WINDOWS_FONTS void InitFont(void); void DestroyFont(void); #endif Bit32u win32_to_bx_key[2][0x100] = { { /* normal-keys */ /* 0x00 - 0x0f */ 0, BX_KEY_ESC, BX_KEY_1, BX_KEY_2, BX_KEY_3, BX_KEY_4, BX_KEY_5, BX_KEY_6, BX_KEY_7, BX_KEY_8, BX_KEY_9, BX_KEY_0, BX_KEY_MINUS, BX_KEY_EQUALS, BX_KEY_BACKSPACE, BX_KEY_TAB, /* 0x10 - 0x1f */ BX_KEY_Q, BX_KEY_W, BX_KEY_E, BX_KEY_R, BX_KEY_T, BX_KEY_Y, BX_KEY_U, BX_KEY_I, BX_KEY_O, BX_KEY_P, BX_KEY_LEFT_BRACKET, BX_KEY_RIGHT_BRACKET, BX_KEY_ENTER, BX_KEY_CTRL_L, BX_KEY_A, BX_KEY_S, /* 0x20 - 0x2f */ BX_KEY_D, BX_KEY_F, BX_KEY_G, BX_KEY_H, BX_KEY_J, BX_KEY_K, BX_KEY_L, BX_KEY_SEMICOLON, BX_KEY_SINGLE_QUOTE, BX_KEY_GRAVE, BX_KEY_SHIFT_L, BX_KEY_BACKSLASH, BX_KEY_Z, BX_KEY_X, BX_KEY_C, BX_KEY_V, /* 0x30 - 0x3f */ BX_KEY_B, BX_KEY_N, BX_KEY_M, BX_KEY_COMMA, BX_KEY_PERIOD, BX_KEY_SLASH, BX_KEY_SHIFT_R, BX_KEY_KP_MULTIPLY, BX_KEY_ALT_L, BX_KEY_SPACE, BX_KEY_CAPS_LOCK, BX_KEY_F1, BX_KEY_F2, BX_KEY_F3, BX_KEY_F4, BX_KEY_F5, /* 0x40 - 0x4f */ BX_KEY_F6, BX_KEY_F7, BX_KEY_F8, BX_KEY_F9, BX_KEY_F10, BX_KEY_PAUSE, BX_KEY_SCRL_LOCK, BX_KEY_KP_HOME, BX_KEY_KP_UP, BX_KEY_KP_PAGE_UP, BX_KEY_KP_SUBTRACT, BX_KEY_KP_LEFT, BX_KEY_KP_5, BX_KEY_KP_RIGHT, BX_KEY_KP_ADD, BX_KEY_KP_END, /* 0x50 - 0x5f */ BX_KEY_KP_DOWN, BX_KEY_KP_PAGE_DOWN, BX_KEY_KP_INSERT, BX_KEY_KP_DELETE, 0, 0, BX_KEY_LEFT_BACKSLASH, BX_KEY_F11, BX_KEY_F12, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */ 0, /* Todo: "Katakana" key (ibm 133) for Japanese 106 keyboard */ 0, 0, 0, /* Todo: "Ro" key (ibm 56) for Japanese 106 keyboard */ 0, 0, 0, 0, 0, 0, /* Todo: "convert" key (ibm 132) for Japanese 106 keyboard */ 0, 0, /* Todo: "non-convert" key (ibm 131) for Japanese 106 keyboard */ 0, 0, /* Todo: "Yen" key (ibm 14) for Japanese 106 keyboard */ 0, 0, }, { /* extended-keys */ /* 0x00 - 0x0f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, BX_KEY_KP_ENTER, BX_KEY_CTRL_R, 0, 0, /* 0x20 - 0x2f */ 0, BX_KEY_POWER_CALC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ 0, 0, BX_KEY_INT_HOME, 0, 0, BX_KEY_KP_DIVIDE, 0, BX_KEY_PRINT, BX_KEY_ALT_R, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ 0, 0, 0, 0, 0, BX_KEY_NUM_LOCK, 0, BX_KEY_HOME, BX_KEY_UP, BX_KEY_PAGE_UP, 0, BX_KEY_LEFT, 0, BX_KEY_RIGHT, 0, BX_KEY_END, /* 0x50 - 0x5f */ BX_KEY_DOWN, BX_KEY_PAGE_DOWN, BX_KEY_INSERT, BX_KEY_DELETE, 0, 0, 0, 0, 0, 0, 0, BX_KEY_WIN_L, BX_KEY_WIN_R, BX_KEY_MENU, BX_KEY_POWER_POWER, BX_KEY_POWER_SLEEP, /* 0x60 - 0x6f */ 0, 0, 0, BX_KEY_POWER_WAKE, 0, BX_KEY_INT_SEARCH, BX_KEY_INT_FAV, 0, BX_KEY_INT_STOP, BX_KEY_INT_FORWARD, BX_KEY_INT_BACK, BX_KEY_POWER_MYCOMP, BX_KEY_INT_MAIL, 0, 0, 0, } }; /* Macro to convert WM_ button state to BX button state */ #if BX_SHOW_IPS VOID CALLBACK MyTimer(HWND,UINT,UINT,DWORD); #endif static void processMouseXY(int x, int y, int z, int windows_state, int implied_state_change) { int bx_state; int old_bx_state; EnterCriticalSection(&stInfo.mouseCS); bx_state=((windows_state & MK_LBUTTON) ? 1 : 0) + ((windows_state & MK_RBUTTON) ? 2 : 0) + ((windows_state & MK_MBUTTON) ? 4 : 0); old_bx_state=bx_state ^ implied_state_change; if (old_bx_state!=mouse_button_state) { /* Make up for missing message */ BX_INFO(("&&&missing mouse state change")); EnterCriticalSection(&stInfo.keyCS); enq_mouse_event(); mouse_button_state=old_bx_state; enq_key_event(mouse_button_state, MOUSE_PRESSED); LeaveCriticalSection(&stInfo.keyCS); } ms_ydelta=ms_savedy-y; ms_xdelta=x-ms_savedx; ms_zdelta=z; ms_lastx=x; ms_lasty=y; if (bx_state!=mouse_button_state) { EnterCriticalSection(&stInfo.keyCS); enq_mouse_event(); mouse_button_state=bx_state; enq_key_event(mouse_button_state, MOUSE_PRESSED); LeaveCriticalSection(&stInfo.keyCS); } LeaveCriticalSection(&stInfo.mouseCS); } static void resetDelta() { EnterCriticalSection(&stInfo.mouseCS); ms_savedx=ms_lastx; ms_savedy=ms_lasty; ms_ydelta=ms_xdelta=ms_zdelta=0; LeaveCriticalSection(&stInfo.mouseCS); } static void cursorWarped() { EnterCriticalSection(&stInfo.mouseCS); EnterCriticalSection(&stInfo.keyCS); enq_mouse_event(); LeaveCriticalSection(&stInfo.keyCS); ms_lastx=stretched_x/2; ms_lasty=stretched_y/2; ms_savedx=ms_lastx; ms_savedy=ms_lasty; LeaveCriticalSection(&stInfo.mouseCS); } // GUI thread must be dead/done in order to call terminateEmul void terminateEmul(int reason) { // We know that Critical Sections were inited when x_tilesize has been set // See bx_win32_gui_c::specific_init if (x_tilesize != 0) { DeleteCriticalSection (&stInfo.drawCS); DeleteCriticalSection (&stInfo.keyCS); DeleteCriticalSection (&stInfo.mouseCS); } x_tilesize = 0; if (MemoryDC) DeleteDC (MemoryDC); if (MemoryBitmap) DeleteObject (MemoryBitmap); if (bitmap_info) delete[] (char*)bitmap_info; for (unsigned b=0; bget_param_enum(BXPN_SEL_CONFIG_INTERFACE)->get_selected(), "win32config"); put("WGUI"); // prepare for possible fullscreen mode desktopWindow = GetDesktopWindow(); GetWindowRect(desktopWindow, &desktop); desktop_x = desktop.right - desktop.left; desktop_y = desktop.bottom - desktop.top; hotKeyReceiver = stInfo.simWnd; BX_INFO(("Desktop Window dimensions: %d x %d", desktop_x, desktop_y)); static RGBQUAD black_quad={ 0, 0, 0, 0}; stInfo.kill = 0; stInfo.UIinited = FALSE; InitializeCriticalSection(&stInfo.drawCS); InitializeCriticalSection(&stInfo.keyCS); InitializeCriticalSection(&stInfo.mouseCS); x_tilesize = tilewidth; y_tilesize = tileheight; bx_bitmap_entries = 0; bx_headerbar_entries = 0; bx_hb_separator = 0; mouseCaptureMode = FALSE; mouseCaptureNew = FALSE; mouseToggleReq = FALSE; // parse win32 specific options if (argc > 1) { for (i = 1; i < argc; i++) { BX_INFO(("option %d: %s", i, argv[i])); if (!strcmp(argv[i], "legacyF12")) { BX_PANIC(("The option 'legacyF12' is now deprecated - use 'mouse: toggle=f12' instead")); #if BX_DEBUGGER && BX_DEBUGGER_GUI } else if (!strcmp(argv[i], "gui_debug")) { if (gui_ci) { gui_debug = TRUE; SIM->set_debug_gui(1); } else { BX_PANIC(("Config interface 'win32config' is required for gui debugger")); } #endif } else { BX_PANIC(("Unknown win32 option '%s'", argv[i])); } } } mouse_buttons = GetSystemMetrics(SM_CMOUSEBUTTONS); BX_INFO(("Number of Mouse Buttons = %d", mouse_buttons)); if ((SIM->get_param_enum(BXPN_MOUSE_TOGGLE)->get() == BX_MOUSE_TOGGLE_CTRL_MB) && (mouse_buttons == 2)) { lstrcpy(szMouseEnable, "CTRL + Lbutton + Rbutton enables mouse "); lstrcpy(szMouseDisable, "CTRL + Lbutton + Rbutton disables mouse"); lstrcpy(szMouseTooltip, "Enable mouse capture\nUse CTRL + Lbutton + Rbutton to release"); } else { wsprintf(szMouseEnable, "%s enables mouse ", get_toggle_info()); wsprintf(szMouseDisable, "%s disables mouse", get_toggle_info()); wsprintf(szMouseTooltip, "Enable mouse capture\nUse %s to release", get_toggle_info()); } stInfo.hInstance = GetModuleHandle(NULL); UNUSED(headerbar_y); dimension_x = 640; dimension_y = 480; current_bpp = 8; stretched_x = dimension_x; stretched_y = dimension_y; stretch_factor = 1; for(unsigned c=0; c<256; c++) vgafont[c] = NULL; create_vga_font(); bitmap_info=(BITMAPINFO*)new char[sizeof(BITMAPINFOHEADER)+ 259*sizeof(RGBQUAD)]; // 256 + 3 entries for 16 bpp mode bitmap_info->bmiHeader.biSize=sizeof(BITMAPINFOHEADER); bitmap_info->bmiHeader.biWidth=x_tilesize; // Height is negative for top-down bitmap bitmap_info->bmiHeader.biHeight= -(LONG)y_tilesize; bitmap_info->bmiHeader.biPlanes=1; bitmap_info->bmiHeader.biBitCount=8; bitmap_info->bmiHeader.biCompression=BI_RGB; bitmap_info->bmiHeader.biSizeImage=x_tilesize*y_tilesize*4; // I think these next two figures don't matter; saying 45 pixels/centimeter bitmap_info->bmiHeader.biXPelsPerMeter=4500; bitmap_info->bmiHeader.biYPelsPerMeter=4500; bitmap_info->bmiHeader.biClrUsed=256; bitmap_info->bmiHeader.biClrImportant=0; cmap_index=bitmap_info->bmiColors; // start out with all color map indeces pointing to Black cmap_index[0] = black_quad; for (i=1; i<259; i++) { cmap_index[i] = cmap_index[0]; } if (stInfo.hInstance) workerThread = _beginthread (UIThread, 0, NULL); else terminateEmul(EXIT_GMH_FAILURE); // Wait for a window before continuing if ((stInfo.kill == 0) && (FindWindow(szAppName, NULL) == NULL)) Sleep(500); // Now set this thread's priority to below normal because this is where // the emulated CPU runs, and it hogs the real CPU SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); if (SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get()) BX_INFO(("private_colormap option ignored.")); // load keymap tables if (SIM->get_param_bool(BXPN_KBD_USEMAPPING)->get()) { bx_keymap.loadKeymap(NULL); // I have no function to convert X windows symbols } if (gui_ci) { dialog_caps = BX_GUI_DLG_ALL; } } void resize_main_window() { RECT R; int toolbar_y = 0; int statusbar_y = 0; unsigned long mainStyle; if (IsWindowVisible(hwndTB)) { toolbarVisible = TRUE; GetWindowRect(hwndTB, &R); toolbar_y = R.bottom - R.top; } if (IsWindowVisible(hwndSB)) { statusVisible = TRUE; GetWindowRect(hwndSB, &R); statusbar_y = R.bottom - R.top; } // stretched_x and stretched_y were set in dimension_update() // if we need to do any additional resizing, do it now if ((desktop_y > 0) && (stretched_y >= (unsigned)desktop_y)) { if (!queryFullScreen) { MessageBox(NULL, "Going into fullscreen mode -- Alt-Enter to revert", "Going fullscreen", MB_APPLMODAL); queryFullScreen = TRUE; } // hide toolbar and status bars to get some additional space ShowWindow(hwndTB, SW_HIDE); ShowWindow(hwndSB, SW_HIDE); // hide title bar mainStyle = GetWindowLong(stInfo.mainWnd, GWL_STYLE); mainStyle &= ~(WS_CAPTION | WS_BORDER); SetWindowLong(stInfo.mainWnd, GWL_STYLE, mainStyle); // maybe need to adjust stInfo.simWnd here also? saveParent = SetParent(stInfo.mainWnd, desktopWindow); if (saveParent) { BX_DEBUG(("Saved parent window")); SetWindowPos(stInfo.mainWnd, HWND_TOPMOST, desktop.left, desktop.top, desktop.right, desktop.bottom, SWP_SHOWWINDOW); } } else { if (saveParent) { BX_DEBUG(("Restoring parent window")); SetParent(stInfo.mainWnd, saveParent); saveParent = NULL; } // put back the title bar, border, etc... mainStyle = GetWindowLong(stInfo.mainWnd, GWL_STYLE); mainStyle |= WS_CAPTION | WS_BORDER; SetWindowLong(stInfo.mainWnd, GWL_STYLE, mainStyle); if (toolbarVisible) ShowWindow(hwndTB, SW_SHOW); if (statusVisible) ShowWindow(hwndSB, SW_SHOW); SetRect(&R, 0, 0, stretched_x, stretched_y); DWORD style = GetWindowLong(stInfo.simWnd, GWL_STYLE); DWORD exstyle = GetWindowLong(stInfo.simWnd, GWL_EXSTYLE); AdjustWindowRectEx(&R, style, FALSE, exstyle); style = GetWindowLong(stInfo.mainWnd, GWL_STYLE); AdjustWindowRect(&R, style, FALSE); SetWindowPos(stInfo.mainWnd, HWND_TOP, 0, 0, R.right - R.left, R.bottom - R.top + toolbar_y + statusbar_y, SWP_NOMOVE | SWP_NOZORDER); } fix_size = FALSE; } // This thread controls the GUI window. VOID CDECL UIThread(PVOID pvoid) { MSG msg; HDC hdc; WNDCLASS wndclass; RECT wndRect; workerThreadID = GetCurrentThreadId(); GetClassInfo(NULL, WC_DIALOG, &wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = mainWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = stInfo.hInstance; wndclass.hIcon = LoadIcon (stInfo.hInstance, MAKEINTRESOURCE(ICON_BOCHS)); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; RegisterClass (&wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = simWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = stInfo.hInstance; wndclass.hIcon = NULL; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "SIMWINDOW"; RegisterClass (&wndclass); SetRect(&wndRect, 0, 0, stretched_x, stretched_y); DWORD sim_style = WS_CHILD; DWORD sim_exstyle = WS_EX_CLIENTEDGE; AdjustWindowRectEx(&wndRect, sim_style, FALSE, sim_exstyle); DWORD main_style = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; AdjustWindowRect(&wndRect, main_style, FALSE); stInfo.mainWnd = CreateWindow (szAppName, szWindowName, main_style, CW_USEDEFAULT, CW_USEDEFAULT, wndRect.right - wndRect.left, wndRect.bottom - wndRect.top, NULL, NULL, stInfo.hInstance, NULL); if (stInfo.mainWnd) { InitCommonControls(); hwndTB = CreateWindowEx(0, TOOLBARCLASSNAME, (LPSTR) NULL, WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT, 0, 0, 0, 0, stInfo.mainWnd, (HMENU) 100, stInfo.hInstance, NULL); SendMessage(hwndTB, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0); SendMessage(hwndTB, TB_SETBITMAPSIZE, 0, (LPARAM)MAKELONG(32, 32)); hwndSB = CreateStatusWindow(WS_CHILD | WS_VISIBLE, "", stInfo.mainWnd, 0x7712); if (hwndSB) { int elements; SB_Edges[0] = SIZE_OF_SB_MOUSE_MESSAGE + SIZE_OF_SB_ELEMENT; #if BX_SHOW_IPS SB_Edges[1] = SB_Edges[0] + SIZE_OF_SB_IPS_MESSAGE; #endif for (elements = BX_SB_TEXT_ELEMENTS; elements < (BX_MAX_STATUSITEMS+BX_SB_TEXT_ELEMENTS); elements++) SB_Edges[elements] = SB_Edges[elements-1] + SIZE_OF_SB_ELEMENT; SB_Edges[elements] = -1; SendMessage(hwndSB, SB_SETPARTS, BX_MAX_STATUSITEMS+BX_SB_TEXT_ELEMENTS+1, (LPARAM)&SB_Edges); } SetStatusText(0, szMouseEnable, TRUE); stInfo.simWnd = CreateWindowEx(sim_exstyle, "SIMWINDOW", "", sim_style, 0, 0, 0, 0, stInfo.mainWnd, NULL, stInfo.hInstance, NULL); /* needed for the Japanese versions of Windows */ if (stInfo.simWnd) { HMODULE hm; hm = GetModuleHandle("USER32"); if (hm) { BOOL (WINAPI *enableime)(HWND, BOOL); enableime = (BOOL (WINAPI *)(HWND, BOOL))GetProcAddress(hm, "WINNLSEnableIME"); if (enableime) { enableime(stInfo.simWnd, FALSE); BX_INFO(("IME disabled")); } } } ShowWindow(stInfo.simWnd, SW_SHOW); SetFocus(stInfo.simWnd); ShowCursor(!mouseCaptureMode); POINT pt = { 0, 0 }; ClientToScreen(stInfo.simWnd, &pt); SetCursorPos(pt.x + stretched_x/2, pt.y + stretched_y/2); cursorWarped(); hdc = GetDC(stInfo.simWnd); MemoryBitmap = CreateCompatibleBitmap(hdc, BX_MAX_XRES, BX_MAX_YRES); MemoryDC = CreateCompatibleDC(hdc); ReleaseDC(stInfo.simWnd, hdc); if (MemoryBitmap && MemoryDC) { resize_main_window(); ShowWindow(stInfo.mainWnd, SW_SHOW); #if BX_DEBUGGER && BX_DEBUGGER_GUI if (gui_debug) { InitDebugDialog(); } #endif #if BX_SHOW_IPS UINT idTimer = 2; SetTimer(stInfo.simWnd, idTimer, 1000, (TIMERPROC)MyTimer); #endif stInfo.UIinited = TRUE; bx_gui->clear_screen(); while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg); DispatchMessage (&msg); } } } stInfo.kill = EXIT_GUI_SHUTDOWN; _endthread(); } void SetStatusText(int Num, const char *Text, bx_bool active, bx_bool w) { char StatText[MAX_PATH]; if ((Num < BX_SB_TEXT_ELEMENTS) || (Num > (BX_MAX_STATUSITEMS+BX_SB_TEXT_ELEMENTS))) { StatText[0] = ' '; // Add space to text in 1st and last items lstrcpy(StatText+1, Text); SendMessage(hwndSB, SB_SETTEXT, Num, (LPARAM)StatText); } else { StatText[0] = 9; // Center the rest lstrcpy(StatText+1, Text); lstrcpy(SB_Text[Num-BX_SB_TEXT_ELEMENTS], StatText); SB_Active[Num-BX_SB_TEXT_ELEMENTS] = active; SB_ActiveW[Num-BX_SB_TEXT_ELEMENTS] = w; SendMessage(hwndSB, SB_SETTEXT, Num | SBT_OWNERDRAW, (LPARAM)SB_Text[Num-BX_SB_TEXT_ELEMENTS]); } UpdateWindow(hwndSB); } void bx_win32_gui_c::statusbar_setitem(int element, bx_bool active, bx_bool w) { if (element < 0) { for (int i = 0; i < (int)statusitem_count; i++) { SetStatusText(i+BX_SB_TEXT_ELEMENTS, statusitem_text[i], active, w); } } else if (element < (int)statusitem_count) { SetStatusText(element+BX_SB_TEXT_ELEMENTS, statusitem_text[element], active, w); } } LRESULT CALLBACK mainWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { DRAWITEMSTRUCT *lpdis; char *sbtext; NMHDR *lpnmh; TOOLTIPTEXT *lpttt; int idTT, hbar_id; switch (iMsg) { case WM_CREATE: SetStatusText(0, szMouseEnable, TRUE); return 0; case WM_COMMAND: if (LOWORD(wParam) >= 101) { EnterCriticalSection(&stInfo.keyCS); enq_key_event(LOWORD(wParam)-101, HEADERBAR_CLICKED); LeaveCriticalSection(&stInfo.keyCS); } break; case WM_SETFOCUS: SetFocus(stInfo.simWnd); return 0; case WM_CLOSE: SendMessage(stInfo.simWnd, WM_CLOSE, 0, 0); break; case WM_DESTROY: PostQuitMessage (0); return 0; case WM_SIZE: { int x, y; SendMessage(hwndTB, TB_AUTOSIZE, 0, 0); SendMessage(hwndSB, WM_SIZE, 0, 0); // now fit simWindow to mainWindow int rect_data[] = { 1, 0, IsWindowVisible(hwndTB), 100, IsWindowVisible(hwndSB), 0x7712, 0, 0 }; RECT R; GetEffectiveClientRect(hwnd, &R, rect_data); x = R.right - R.left; y = R.bottom - R.top; MoveWindow(stInfo.simWnd, R.left, R.top, x, y, TRUE); GetClientRect(stInfo.simWnd, &R); x = R.right - R.left; y = R.bottom - R.top; if ((x != (int)stretched_x) || (y != (int)stretched_y)) { BX_ERROR(("Sim client size(%d, %d) != stretched size(%d, %d)!", x, y, stretched_x, stretched_y)); if (!saveParent) fix_size = TRUE; // no fixing if fullscreen } } break; case WM_DRAWITEM: lpdis = (DRAWITEMSTRUCT *)lParam; if (lpdis->hwndItem == hwndSB) { sbtext = (char *)lpdis->itemData; if (SB_Active[lpdis->itemID-BX_SB_TEXT_ELEMENTS]) { if (SB_ActiveW[lpdis->itemID-BX_SB_TEXT_ELEMENTS]) SetBkColor(lpdis->hDC, 0x000040FF); else SetBkColor(lpdis->hDC, 0x0000FF00); } else { SetBkMode(lpdis->hDC, TRANSPARENT); SetTextColor(lpdis->hDC, 0x00808080); } DrawText(lpdis->hDC, sbtext+1, lstrlen(sbtext)-1, &lpdis->rcItem, DT_SINGLELINE | DT_CENTER | DT_VCENTER); return TRUE; } break; case WM_NOTIFY: lpnmh = (LPNMHDR)lParam; if (lpnmh->code == TTN_NEEDTEXT) { lpttt = (LPTOOLTIPTEXT)lParam; idTT = (int)wParam; hbar_id = idTT - 101; if (SendMessage(hwndTB, TB_GETSTATE, idTT, 0) && bx_headerbar_entry[hbar_id].tooltip != NULL) { lstrcpy(lpttt->szText, bx_headerbar_entry[hbar_id].tooltip); } } return FALSE; break; } return DefWindowProc(hwnd, iMsg, wParam, lParam); } void SetMouseCapture() { POINT pt = {0, 0}; if (mouseToggleReq) { mouseCaptureMode = mouseCaptureNew; mouseToggleReq = FALSE; } else { SIM->get_param_bool(BXPN_MOUSE_ENABLED)->set(mouseCaptureMode); } ShowCursor(!mouseCaptureMode); ShowCursor(!mouseCaptureMode); // somehow one didn't do the trick (win98) ClientToScreen(stInfo.simWnd, &pt); SetCursorPos(pt.x + stretched_x/2, pt.y + stretched_y/2); cursorWarped(); if (mouseCaptureMode) SetStatusText(0, szMouseDisable, TRUE); else SetStatusText(0, szMouseEnable, TRUE); } LRESULT CALLBACK simWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { HDC hdc, hdcMem; PAINTSTRUCT ps; POINT pt; bx_bool mouse_toggle = 0; static BOOL mouseModeChange = FALSE; switch (iMsg) { case WM_CREATE: #if BX_USE_WINDOWS_FONTS InitFont(); #endif SetTimer (hwnd, 1, 330, NULL); return 0; case WM_TIMER: if (mouseToggleReq && (GetActiveWindow() == stInfo.mainWnd)) { SetMouseCapture(); } // If mouse escaped, bring it back if (mouseCaptureMode) { pt.x = 0; pt.y = 0; ClientToScreen(hwnd, &pt); SetCursorPos(pt.x + stretched_x/2, pt.y + stretched_y/2); cursorWarped(); } if (fix_size) { resize_main_window(); } return 0; case WM_PAINT: EnterCriticalSection(&stInfo.drawCS); hdc = BeginPaint (hwnd, &ps); hdcMem = CreateCompatibleDC (hdc); SelectObject (hdcMem, MemoryBitmap); if (stretch_factor == 1) { BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left + 1, ps.rcPaint.bottom - ps.rcPaint.top + 1, hdcMem, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY); } else { StretchBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left + 1, ps.rcPaint.bottom - ps.rcPaint.top + 1, hdcMem, ps.rcPaint.left/stretch_factor, ps.rcPaint.top, (ps.rcPaint.right - ps.rcPaint.left+1)/stretch_factor, (ps.rcPaint.bottom - ps.rcPaint.top+1), SRCCOPY); } DeleteDC (hdcMem); EndPaint (hwnd, &ps); LeaveCriticalSection(&stInfo.drawCS); return 0; case WM_MOUSEMOVE: if (!mouseModeChange) { processMouseXY(LOWORD(lParam), HIWORD(lParam), 0, wParam, 0); } return 0; case WM_MOUSEWHEEL: if (!mouseModeChange) { // WM_MOUSEWHEEL returns x and y relative to the main screen. // WM_MOUSEMOVE below returns x and y relative to the current view. POINT pt; pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); ScreenToClient(stInfo.simWnd, &pt); processMouseXY(pt.x, pt.y, (Bit16s) HIWORD(wParam) / 120, LOWORD(wParam), 0); } return 0; case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: if (mouse_buttons == 2) { if ((wParam & MK_LBUTTON) == MK_LBUTTON) { if (bx_gui->mouse_toggle_check(BX_MT_LBUTTON, 1)) { mouseCaptureMode = !mouseCaptureMode; SetMouseCapture(); mouseModeChange = TRUE; } } else if (mouseModeChange && (iMsg == WM_LBUTTONUP)) { bx_gui->mouse_toggle_check(BX_MT_LBUTTON, 0); mouseModeChange = FALSE; } else { processMouseXY(LOWORD(lParam), HIWORD(lParam), 0, wParam, 1); } return 0; } processMouseXY(LOWORD(lParam), HIWORD(lParam), 0, wParam, 1); return 0; case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP: if ((wParam & MK_MBUTTON) == MK_MBUTTON) { if (bx_gui->mouse_toggle_check(BX_MT_MBUTTON, 1)) { mouseCaptureMode = !mouseCaptureMode; SetMouseCapture(); mouseModeChange = TRUE; } } else if (mouseModeChange && (iMsg == WM_MBUTTONUP)) { bx_gui->mouse_toggle_check(BX_MT_MBUTTON, 0); mouseModeChange = FALSE; } else { processMouseXY(LOWORD(lParam), HIWORD(lParam), 0, wParam, 4); } return 0; case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: if (mouse_buttons == 2) { if ((wParam & MK_RBUTTON) == MK_RBUTTON) { if (bx_gui->mouse_toggle_check(BX_MT_RBUTTON, 1)) { mouseCaptureMode = !mouseCaptureMode; SetMouseCapture(); mouseModeChange = TRUE; } } else if (mouseModeChange && (iMsg == WM_RBUTTONUP)) { bx_gui->mouse_toggle_check(BX_MT_RBUTTON, 0); mouseModeChange = FALSE; } else { processMouseXY(LOWORD(lParam), HIWORD(lParam), 0, wParam, 2); } return 0; } processMouseXY(LOWORD(lParam), HIWORD(lParam), 0, wParam, 2); return 0; case WM_CLOSE: return DefWindowProc (hwnd, iMsg, wParam, lParam); case WM_DESTROY: KillTimer (hwnd, 1); stInfo.UIinited = FALSE; #if BX_USE_WINDOWS_FONTS DestroyFont(); #endif return 0; case WM_KEYDOWN: case WM_SYSKEYDOWN: if (wParam == VK_CONTROL) { mouse_toggle = bx_gui->mouse_toggle_check(BX_MT_KEY_CTRL, 1); } else if (wParam == VK_MENU) { mouse_toggle = bx_gui->mouse_toggle_check(BX_MT_KEY_ALT, 1); } else if (wParam == VK_F10) { mouse_toggle = bx_gui->mouse_toggle_check(BX_MT_KEY_F10, 1); } else if (wParam == VK_F12) { mouse_toggle = bx_gui->mouse_toggle_check(BX_MT_KEY_F12, 1); } if (mouse_toggle) { mouseCaptureMode = !mouseCaptureMode; SetMouseCapture(); return 0; } EnterCriticalSection(&stInfo.keyCS); enq_key_event(HIWORD (lParam) & 0x01FF, BX_KEY_PRESSED); LeaveCriticalSection(&stInfo.keyCS); return 0; case WM_KEYUP: case WM_SYSKEYUP: // check if it's keyup, alt key, non-repeat // see http://msdn2.microsoft.com/en-us/library/ms646267.aspx if ((wParam == VK_RETURN) && ((HIWORD(lParam) & BX_SYSKEY) == (KF_ALTDOWN | KF_UP))) { if (!saveParent) { BX_INFO(("entering fullscreen mode")); theGui->dimension_update(desktop_x, desktop_y, 0, 0, current_bpp); } else { BX_INFO(("leaving fullscreen mode")); theGui->dimension_update(dimension_x, desktop_y - 1, 0, 0, current_bpp); } } else { if (wParam == VK_CONTROL) { bx_gui->mouse_toggle_check(BX_MT_KEY_CTRL, 0); } else if (wParam == VK_MENU) { bx_gui->mouse_toggle_check(BX_MT_KEY_ALT, 0); } else if (wParam == VK_F10) { bx_gui->mouse_toggle_check(BX_MT_KEY_F10, 0); } else if (wParam == VK_F12) { bx_gui->mouse_toggle_check(BX_MT_KEY_F12, 0); } EnterCriticalSection(&stInfo.keyCS); enq_key_event(HIWORD (lParam) & 0x01FF, BX_KEY_RELEASED); LeaveCriticalSection(&stInfo.keyCS); } return 0; case WM_SYSCHAR: // check if it's keydown, alt key, non-repeat // see http://msdn2.microsoft.com/en-us/library/ms646267.aspx if (wParam == VK_RETURN) { if ((HIWORD(lParam) & BX_SYSKEY) == KF_ALTDOWN) { if (!saveParent) { BX_INFO(("entering fullscreen mode")); theGui->dimension_update(desktop_x, desktop_y, 0, 0, current_bpp); } else { BX_INFO(("leaving fullscreen mode")); theGui->dimension_update(dimension_x, desktop_y - 1, 0, 0, current_bpp); } } } case WM_CHAR: case WM_DEADCHAR: case WM_SYSDEADCHAR: return 0; } return DefWindowProc (hwnd, iMsg, wParam, lParam); } void enq_key_event(Bit32u key, Bit32u press_release) { static BOOL alt_pressed_l = FALSE; static BOOL alt_pressed_r = FALSE; static BOOL ctrl_pressed_l = FALSE; static BOOL ctrl_pressed_r = FALSE; static BOOL shift_pressed_l = FALSE; static BOOL shift_pressed_r = FALSE; // Windows generates multiple keypresses when holding down these keys if (press_release == BX_KEY_PRESSED) { switch (key) { case 0x1d: if (ctrl_pressed_l) return; ctrl_pressed_l = TRUE; break; case 0x2a: if (shift_pressed_l) return; shift_pressed_l = TRUE; break; case 0x36: if (shift_pressed_r) return; shift_pressed_r = TRUE; break; case 0x38: if (alt_pressed_l) return; alt_pressed_l = TRUE; break; case 0x011d: if (ctrl_pressed_r) return; ctrl_pressed_r = TRUE; break; case 0x0138: if (alt_pressed_r) return; // This makes the "AltGr" key on European keyboards work if (ctrl_pressed_l) { enq_key_event(0x1d, BX_KEY_RELEASED); } alt_pressed_r = TRUE; break; } } else { switch (key) { case 0x1d: if (!ctrl_pressed_l) return; ctrl_pressed_l = FALSE; break; case 0x2a: shift_pressed_l = FALSE; break; case 0x36: shift_pressed_r = FALSE; break; case 0x38: alt_pressed_l = FALSE; break; case 0x011d: ctrl_pressed_r = FALSE; break; case 0x0138: alt_pressed_r = FALSE; break; } } if (((tail+1) % SCANCODE_BUFSIZE) == head) { BX_ERROR(("enq_scancode: buffer full")); return; } keyevents[tail].key_event = key | press_release; tail = (tail + 1) % SCANCODE_BUFSIZE; } void enq_mouse_event(void) { EnterCriticalSection(&stInfo.mouseCS); if (ms_xdelta || ms_ydelta || ms_zdelta) { if (((tail+1) % SCANCODE_BUFSIZE) == head) { LeaveCriticalSection(&stInfo.mouseCS); BX_ERROR(("enq_scancode: buffer full")); return; } QueueEvent& current=keyevents[tail]; current.key_event=MOUSE_MOTION; current.mouse_x=ms_xdelta; current.mouse_y=ms_ydelta; current.mouse_z=ms_zdelta; current.mouse_button_state=mouse_button_state; resetDelta(); tail = (tail + 1) % SCANCODE_BUFSIZE; } LeaveCriticalSection(&stInfo.mouseCS); } QueueEvent* deq_key_event(void) { QueueEvent* key; if (head == tail) { BX_ERROR(("deq_scancode: buffer empty")); return((QueueEvent*)0); } key = &keyevents[head]; head = (head + 1) % SCANCODE_BUFSIZE; return(key); } // ::HANDLE_EVENTS() // // Called periodically (vga_update_interval in .bochsrc) so the // the gui code can poll for keyboard, mouse, and other // relevant events. void bx_win32_gui_c::handle_events(void) { Bit32u key; Bit32u key_event; if (stInfo.kill) terminateEmul(stInfo.kill); // Handle mouse moves enq_mouse_event(); // Handle keyboard and mouse clicks EnterCriticalSection(&stInfo.keyCS); while (head != tail) { QueueEvent* queue_event=deq_key_event(); if (! queue_event) break; key = queue_event->key_event; if (key==MOUSE_MOTION) { DEV_mouse_motion_ext(queue_event->mouse_x, queue_event->mouse_y, queue_event->mouse_z, queue_event->mouse_button_state); } // Check for mouse buttons first else if (key & MOUSE_PRESSED) { DEV_mouse_motion_ext(0, 0, 0, LOWORD(key)); } else if (key & HEADERBAR_CLICKED) { headerbar_click(LOWORD(key)); } else { key_event = win32_to_bx_key[(key & 0x100) ? 1 : 0][key & 0xff]; if (key & BX_KEY_RELEASED) key_event |= BX_KEY_RELEASED; DEV_kbd_gen_scancode(key_event); } } LeaveCriticalSection(&stInfo.keyCS); #if BX_SHOW_IPS if (ipsUpdate) { SetStatusText(1, ipsText, 1); ipsUpdate = FALSE; } #endif } // ::FLUSH() // // Called periodically, requesting that the gui code flush all pending // screen update requests. void bx_win32_gui_c::flush(void) { EnterCriticalSection(&stInfo.drawCS); if (updated_area_valid) { // slight bugfix updated_area.right++; updated_area.bottom++; InvalidateRect(stInfo.simWnd, &updated_area, FALSE); updated_area_valid = FALSE; } LeaveCriticalSection(&stInfo.drawCS); } // ::CLEAR_SCREEN() // // Called to request that the VGA region is cleared. Don't // clear the area that defines the headerbar. void bx_win32_gui_c::clear_screen(void) { HGDIOBJ oldObj; if (!stInfo.UIinited) return; EnterCriticalSection(&stInfo.drawCS); oldObj = SelectObject(MemoryDC, MemoryBitmap); PatBlt(MemoryDC, 0, 0, stretched_x, stretched_y, BLACKNESS); SelectObject(MemoryDC, oldObj); updateUpdated(0, 0, dimension_x-1, dimension_y-1); LeaveCriticalSection(&stInfo.drawCS); } // ::TEXT_UPDATE() // // Called in a VGA text mode, to update the screen with // new content. // // old_text: array of character/attributes making up the contents // of the screen from the last call. See below // new_text: array of character/attributes making up the current // contents, which should now be displayed. See below // // format of old_text & new_text: each is tm_info.line_offset*text_rows // bytes long. Each character consists of 2 bytes. The first by is // the character value, the second is the attribute byte. // // cursor_x: new x location of cursor // cursor_y: new y location of cursor // tm_info: this structure contains information for additional // features in text mode (cursor shape, line offset,...) void bx_win32_gui_c::text_update(Bit8u *old_text, Bit8u *new_text, unsigned long cursor_x, unsigned long cursor_y, bx_vga_tminfo_t tm_info) { HDC hdc; unsigned char data[64]; Bit8u *old_line, *new_line; Bit8u cAttr, cChar; unsigned int curs, hchars, i, offset, rows, x, y, xc, yc; BOOL forceUpdate = FALSE, blink_state, blink_mode; #if !BX_USE_WINDOWS_FONTS Bit8u *text_base; Bit8u cfwidth, cfheight, cfheight2, font_col, font_row, font_row2; Bit8u split_textrow, split_fontrows; unsigned int yc2, cs_y; BOOL split_screen; #endif if (!stInfo.UIinited) return; EnterCriticalSection(&stInfo.drawCS); blink_mode = (tm_info.blink_flags & BX_TEXT_BLINK_MODE) > 0; blink_state = (tm_info.blink_flags & BX_TEXT_BLINK_STATE) > 0; if (blink_mode) { if (tm_info.blink_flags & BX_TEXT_BLINK_TOGGLE) forceUpdate = 1; } if (charmap_updated) { for (unsigned c = 0; c<256; c++) { if (char_changed[c]) { memset(data, 0, sizeof(data)); BOOL gfxchar = tm_info.line_graphics && ((c & 0xE0) == 0xC0); for (i=0; i<(unsigned)yChar; i++) { data[i*2] = vga_charmap[c*32+i]; if (gfxchar) { data[i*2+1] = (data[i*2] << 7); } } SetBitmapBits(vgafont[c], 64, data); char_changed[c] = 0; } } forceUpdate = TRUE; charmap_updated = 0; } for (i=0; i<16; i++) { text_pal_idx[i] = DEV_vga_get_actl_pal_idx(i); } hdc = GetDC(stInfo.simWnd); #if !BX_USE_WINDOWS_FONTS if((tm_info.h_panning != h_panning) || (tm_info.v_panning != v_panning)) { forceUpdate = 1; h_panning = tm_info.h_panning; v_panning = tm_info.v_panning; } if(tm_info.line_compare != line_compare) { forceUpdate = 1; line_compare = tm_info.line_compare; } #endif // first invalidate character at previous and new cursor location if((prev_cursor_y < text_rows) && (prev_cursor_x < text_cols)) { curs = prev_cursor_y * tm_info.line_offset + prev_cursor_x * 2; old_text[curs] = ~new_text[curs]; } if((tm_info.cs_start <= tm_info.cs_end) && (tm_info.cs_start < yChar) && (cursor_y < text_rows) && (cursor_x < text_cols)) { curs = cursor_y * tm_info.line_offset + cursor_x * 2; old_text[curs] = ~new_text[curs]; } else { curs = 0xffff; } #if !BX_USE_WINDOWS_FONTS rows = text_rows; if (v_panning) rows++; y = 0; cs_y = 0; text_base = new_text - tm_info.start_address; if (line_compare < dimension_y) { split_textrow = (line_compare + v_panning) / yChar; split_fontrows = ((line_compare + v_panning) % yChar) + 1; } else { split_textrow = rows + 1; split_fontrows = 0; } split_screen = 0; do { hchars = text_cols; if (h_panning) hchars++; if (split_screen) { yc = line_compare + cs_y * yChar + 1; font_row = 0; if (rows == 1) { cfheight = (dimension_y - line_compare - 1) % yChar; if (cfheight == 0) cfheight = yChar; } else { cfheight = yChar; } } else if (v_panning) { if (y == 0) { yc = 0; font_row = v_panning; cfheight = yChar - v_panning; } else { yc = y * yChar - v_panning; font_row = 0; if (rows == 1) { cfheight = v_panning; } else { cfheight = yChar; } } } else { yc = y * yChar; font_row = 0; cfheight = yChar; } if (!split_screen && (y == split_textrow)) { if (split_fontrows < cfheight) cfheight = split_fontrows; } new_line = new_text; old_line = old_text; x = 0; offset = cs_y * tm_info.line_offset; do { if (h_panning) { if (hchars > text_cols) { xc = 0; font_col = h_panning; cfwidth = xChar - h_panning; } else { xc = x * xChar - h_panning; font_col = 0; if (hchars == 1) { cfwidth = h_panning; } else { cfwidth = xChar; } } } else { xc = x * xChar; font_col = 0; cfwidth = xChar; } if (forceUpdate || (old_text[0] != new_text[0]) || (old_text[1] != new_text[1])) { cChar = new_text[0]; if (blink_mode) { cAttr = new_text[1] & 0x7F; if (!blink_state && (new_text[1] & 0x80)) cAttr = (cAttr & 0x70) | (cAttr >> 4); } else { cAttr = new_text[1]; } DrawBitmap(hdc, vgafont[cChar], xc, yc, cfwidth, cfheight, font_col, font_row, SRCCOPY, cAttr); if (offset == curs) { if (font_row == 0) { yc2 = yc + tm_info.cs_start; font_row2 = tm_info.cs_start; cfheight2 = tm_info.cs_end - tm_info.cs_start + 1; } else { if (v_panning > tm_info.cs_start) { yc2 = yc; font_row2 = font_row; cfheight2 = tm_info.cs_end - v_panning + 1; } else { yc2 = yc + tm_info.cs_start - v_panning; font_row2 = tm_info.cs_start; cfheight2 = tm_info.cs_end - tm_info.cs_start + 1; } } cAttr = ((cAttr >> 4) & 0xF) + ((cAttr & 0xF) << 4); DrawBitmap(hdc, vgafont[cChar], xc, yc2, cfwidth, cfheight2, font_col, font_row2, SRCCOPY, cAttr); } } x++; new_text+=2; old_text+=2; offset+=2; } while (--hchars); if (!split_screen && (y == split_textrow)) { new_text = text_base; forceUpdate = 1; cs_y = 0; if (tm_info.split_hpanning) h_panning = 0; rows = ((dimension_y - line_compare + yChar - 2) / yChar) + 1; split_screen = 1; } else { y++; cs_y++; new_text = new_line + tm_info.line_offset; old_text = old_line + tm_info.line_offset; } } while (--rows); h_panning = tm_info.h_panning; #else rows = text_rows; y = 0; do { hchars = text_cols; yc = y * yChar; new_line = new_text; old_line = old_text; x = 0; offset = y * tm_info.line_offset; do { xc = x * xChar; if (forceUpdate || (old_text[0] != new_text[0]) || (old_text[1] != new_text[1])) { cChar = new_text[0]; if (blink_mode) { cAttr = new_text[1] & 0x7F; if (!blink_state && (new_text[1] & 0x80)) cAttr = (cAttr & 0x70) | (cAttr >> 4); } else { cAttr = new_text[1]; } DrawChar(hdc, cChar, xc, yc, cAttr, 1, 0); if (offset == curs) { DrawChar(hdc, cChar, xc, yc, cAttr, tm_info.cs_start, tm_info.cs_end); } } x++; new_text+=2; old_text+=2; offset+=2; } while (--hchars); y++; new_text = new_line + tm_info.line_offset; old_text = old_line + tm_info.line_offset; } while (--rows); #endif prev_cursor_x = cursor_x; prev_cursor_y = cursor_y; ReleaseDC(stInfo.simWnd, hdc); LeaveCriticalSection(&stInfo.drawCS); } int bx_win32_gui_c::get_clipboard_text(Bit8u **bytes, Bit32s *nbytes) { if (OpenClipboard(stInfo.simWnd)) { HGLOBAL hg = GetClipboardData(CF_TEXT); char *data = (char *)GlobalLock(hg); *nbytes = strlen(data); *bytes = new Bit8u[1 + *nbytes]; BX_INFO (("found %d bytes on the clipboard", *nbytes)); memcpy (*bytes, data, *nbytes+1); BX_INFO (("first byte is 0x%02x", *bytes[0])); GlobalUnlock(hg); CloseClipboard(); return 1; // *bytes will be freed in bx_keyb_c::paste_bytes or // bx_keyb_c::service_paste_buf, using delete []. } else { BX_ERROR (("paste: could not open clipboard")); return 0; } } int bx_win32_gui_c::set_clipboard_text(char *text_snapshot, Bit32u len) { if (OpenClipboard(stInfo.simWnd)) { HANDLE hMem = GlobalAlloc(GMEM_ZEROINIT, len); EmptyClipboard(); lstrcpy((char *)hMem, text_snapshot); SetClipboardData(CF_TEXT, hMem); CloseClipboard(); GlobalFree(hMem); return 1; } else { BX_ERROR (("copy: could not open clipboard")); return 0; } } // ::PALETTE_CHANGE() // // Allocate a color in the native GUI, for this color, and put // it in the colormap location 'index'. // returns: 0=no screen update needed (color map change has direct effect) // 1=screen updated needed (redraw using current colormap) bx_bool bx_win32_gui_c::palette_change(unsigned index, unsigned red, unsigned green, unsigned blue) { if ((current_bpp == 16) && (index < 3)) { cmap_index[256+index].rgbRed = red; cmap_index[256+index].rgbBlue = blue; cmap_index[256+index].rgbGreen = green; return 0; } else { cmap_index[index].rgbRed = red; cmap_index[index].rgbBlue = blue; cmap_index[index].rgbGreen = green; } return(1); } // ::GRAPHICS_TILE_UPDATE() // // Called to request that a tile of graphics be drawn to the // screen, since info in this region has changed. // // tile: array of 8bit values representing a block of pixels with // dimension equal to the 'tilewidth' & 'tileheight' parameters to // ::specific_init(). Each value specifies an index into the // array of colors you allocated for ::palette_change() // x0: x origin of tile // y0: y origin of tile // // note: origin of tile and of window based on (0,0) being in the upper // left of the window. void bx_win32_gui_c::graphics_tile_update(Bit8u *tile, unsigned x0, unsigned y0) { HDC hdc; HGDIOBJ oldObj; EnterCriticalSection(&stInfo.drawCS); hdc = GetDC(stInfo.simWnd); oldObj = SelectObject(MemoryDC, MemoryBitmap); StretchDIBits(MemoryDC, x0, y0, x_tilesize, y_tilesize, 0, 0, x_tilesize, y_tilesize, tile, bitmap_info, DIB_RGB_COLORS, SRCCOPY); SelectObject(MemoryDC, oldObj); updateUpdated(x0, y0, x0 + x_tilesize - 1, y0 + y_tilesize - 1); ReleaseDC(stInfo.simWnd, hdc); LeaveCriticalSection(&stInfo.drawCS); } // ::DIMENSION_UPDATE() // // Called when the VGA mode changes it's X,Y dimensions. // Resize the window to this size, but you need to add on // the height of the headerbar to the Y value. // // x: new VGA x size // y: new VGA y size (add headerbar_y parameter from ::specific_init(). // fheight: new VGA character height in text mode // fwidth : new VGA character width in text mode // bpp : bits per pixel in graphics mode void bx_win32_gui_c::dimension_update(unsigned x, unsigned y, unsigned fheight, unsigned fwidth, unsigned bpp) { BxTextMode = (fheight > 0); if (BxTextMode) { text_cols = x / fwidth; text_rows = y / fheight; #if BX_USE_WINDOWS_FONTS if (fheight >= 14) { FontId = 2; xChar = 8; yChar = 16; } else if (fheight >= 12) { FontId = 1; xChar = 8; yChar = 14; } else { FontId = 0; xChar = 8; yChar = 12; } if (fwidth != xChar) { x = x * 8 / fwidth; } if (fheight != yChar) { y = y * yChar / fheight; } #else xChar = fwidth; yChar = fheight; #endif } if (x==dimension_x && y==dimension_y && bpp==current_bpp) return; dimension_x = x; dimension_y = y; stretched_x = dimension_x; stretched_y = dimension_y; stretch_factor = 1; if (BxTextMode && (stretched_x<400)) { stretched_x *= 2; stretch_factor *= 2; } bitmap_info->bmiHeader.biBitCount = bpp; if (bpp == 16) { bitmap_info->bmiHeader.biCompression = BI_BITFIELDS; static RGBQUAD red_mask = {0x00, 0xF8, 0x00, 0x00}; static RGBQUAD green_mask = {0xE0, 0x07, 0x00, 0x00}; static RGBQUAD blue_mask = {0x1F, 0x00, 0x00, 0x00}; bitmap_info->bmiColors[256] = bitmap_info->bmiColors[0]; bitmap_info->bmiColors[257] = bitmap_info->bmiColors[1]; bitmap_info->bmiColors[258] = bitmap_info->bmiColors[2]; bitmap_info->bmiColors[0] = red_mask; bitmap_info->bmiColors[1] = green_mask; bitmap_info->bmiColors[2] = blue_mask; } else { if (current_bpp == 16) { bitmap_info->bmiColors[0] = bitmap_info->bmiColors[256]; bitmap_info->bmiColors[1] = bitmap_info->bmiColors[257]; bitmap_info->bmiColors[2] = bitmap_info->bmiColors[258]; } bitmap_info->bmiHeader.biCompression = BI_RGB; if (bpp == 15) { bitmap_info->bmiHeader.biBitCount = 16; } } current_bpp = bpp; resize_main_window(); BX_INFO(("dimension update x=%d y=%d fontheight=%d fontwidth=%d bpp=%d", x, y, fheight, fwidth, bpp)); host_xres = x; host_yres = y; host_bpp = bpp; } // ::CREATE_BITMAP() // // Create a monochrome bitmap of size 'xdim' by 'ydim', which will // be drawn in the headerbar. Return an integer ID to the bitmap, // with which the bitmap can be referenced later. // // bmap: packed 8 pixels-per-byte bitmap. The pixel order is: // bit0 is the left most pixel, bit7 is the right most pixel. // xdim: x dimension of bitmap // ydim: y dimension of bitmap unsigned bx_win32_gui_c::create_bitmap(const unsigned char *bmap, unsigned xdim, unsigned ydim) { unsigned char *data; TBADDBITMAP tbab; if (bx_bitmap_entries >= BX_MAX_PIXMAPS) terminateEmul(EXIT_HEADER_BITMAP_ERROR); bx_bitmaps[bx_bitmap_entries].bmap = CreateBitmap(xdim,ydim,1,1,NULL); if (!bx_bitmaps[bx_bitmap_entries].bmap) terminateEmul(EXIT_HEADER_BITMAP_ERROR); data = new unsigned char[ydim * xdim/8]; for (unsigned i=0; i BX_MAX_HEADERBAR_ENTRIES) terminateEmul(EXIT_HEADER_BITMAP_ERROR); bx_headerbar_entries++; hb_index = bx_headerbar_entries - 1; memset(tbb,0,sizeof(tbb)); if (bx_hb_separator==0) { tbb[0].iBitmap = 0; tbb[0].idCommand = 0; tbb[0].fsState = 0; tbb[0].fsStyle = TBSTYLE_SEP; SendMessage(hwndTB, TB_ADDBUTTONS, 1,(LPARAM)(LPTBBUTTON)&tbb); } tbb[0].iBitmap = bmap_id; tbb[0].idCommand = hb_index + 101; tbb[0].fsState = TBSTATE_ENABLED; tbb[0].fsStyle = TBSTYLE_BUTTON; if (alignment == BX_GRAVITY_LEFT) { SendMessage(hwndTB, TB_INSERTBUTTON, bx_hb_separator,(LPARAM)(LPTBBUTTON)&tbb); bx_hb_separator++; } else { // BX_GRAVITY_RIGHT SendMessage(hwndTB, TB_INSERTBUTTON, bx_hb_separator+1, (LPARAM)(LPTBBUTTON)&tbb); } bx_headerbar_entry[hb_index].bmap_id = bmap_id; bx_headerbar_entry[hb_index].f = f; bx_headerbar_entry[hb_index].tooltip = NULL; return(hb_index); } // ::SHOW_HEADERBAR() // // Show (redraw) the current headerbar, which is composed of // currently installed bitmaps. void bx_win32_gui_c::show_headerbar(void) { if (!IsWindowVisible(hwndTB)) { SendMessage(hwndTB, TB_AUTOSIZE, 0, 0); ShowWindow(hwndTB, SW_SHOW); resize_main_window(); bx_gui->set_tooltip(bx_gui->get_mouse_headerbar_id(), szMouseTooltip); } } // ::REPLACE_BITMAP() // // Replace the bitmap installed in the headerbar ID slot 'hbar_id', // with the one specified by 'bmap_id'. 'bmap_id' will have // been generated by ::create_bitmap(). The old and new bitmap // must be of the same size. This allows the bitmap the user // sees to change, when some action occurs. For example when // the user presses on the floppy icon, it then displays // the ejected status. // // hbar_id: headerbar slot ID // bmap_id: bitmap ID void bx_win32_gui_c::replace_bitmap(unsigned hbar_id, unsigned bmap_id) { if (bmap_id != bx_headerbar_entry[hbar_id].bmap_id) { bx_headerbar_entry[hbar_id].bmap_id = bmap_id; bx_bool is_visible = IsWindowVisible(hwndTB); if (is_visible) { ShowWindow(hwndTB, SW_HIDE); } SendMessage(hwndTB, TB_CHANGEBITMAP, (WPARAM)hbar_id+101, (LPARAM) MAKELPARAM(bmap_id, 0)); SendMessage(hwndTB, TB_AUTOSIZE, 0, 0); if (is_visible) { ShowWindow(hwndTB, SW_SHOW); } } } // ::EXIT() // // Called before bochs terminates, to allow for a graceful // exit from the native GUI mechanism. void bx_win32_gui_c::exit(void) { printf("# In bx_win32_gui_c::exit(void)!\n"); // kill thread first... PostMessage(stInfo.mainWnd, WM_CLOSE, 0, 0); // Wait until it dies while ((stInfo.kill == 0) && (workerThreadID != 0)) Sleep(500); if (!stInfo.kill) terminateEmul(EXIT_NORMAL); } void create_vga_font(void) { unsigned char data[64]; // VGA font is 8 or 9 wide and up to 32 high for (unsigned c = 0; c<256; c++) { vgafont[c] = CreateBitmap(9,32,1,1,NULL); if (!vgafont[c]) terminateEmul(EXIT_FONT_BITMAP_ERROR); memset(data, 0, sizeof(data)); for (unsigned i=0; i<16; i++) data[i*2] = reverse_bitorder(bx_vgafont[c].data[i]); SetBitmapBits(vgafont[c], 64, data); } } unsigned char reverse_bitorder(unsigned char b) { unsigned char ret=0; for (unsigned i=0; i<8; i++) { ret |= (b & 0x01) << (7-i); b >>= 1; } return(ret); } COLORREF GetColorRef(unsigned char attr) { Bit8u pal_idx = text_pal_idx[attr]; return RGB(cmap_index[pal_idx].rgbRed, cmap_index[pal_idx].rgbGreen, cmap_index[pal_idx].rgbBlue); } void DrawBitmap(HDC hdc, HBITMAP hBitmap, int xStart, int yStart, int width, int height, int fcol, int frow, DWORD dwRop, unsigned char cColor) { BITMAP bm; HDC hdcMem; POINT ptSize, ptOrg; HGDIOBJ oldObj; hdcMem = CreateCompatibleDC (hdc); SelectObject (hdcMem, hBitmap); SetMapMode (hdcMem, GetMapMode (hdc)); GetObject (hBitmap, sizeof (BITMAP), (LPVOID) &bm); ptSize.x = width; ptSize.y = height; DPtoLP (hdc, &ptSize, 1); ptOrg.x = fcol; ptOrg.y = frow; DPtoLP (hdcMem, &ptOrg, 1); oldObj = SelectObject(MemoryDC, MemoryBitmap); COLORREF crFore = SetTextColor(MemoryDC, GetColorRef((cColor>>4)&0xf)); COLORREF crBack = SetBkColor(MemoryDC, GetColorRef(cColor&0xf)); BitBlt(MemoryDC, xStart, yStart, ptSize.x, ptSize.y, hdcMem, ptOrg.x, ptOrg.y, dwRop); SetBkColor(MemoryDC, crBack); SetTextColor(MemoryDC, crFore); SelectObject(MemoryDC, oldObj); updateUpdated(xStart, yStart, ptSize.x + xStart - 1, ptSize.y + yStart - 1); DeleteDC (hdcMem); } void updateUpdated(int x1, int y1, int x2, int y2) { x1*=stretch_factor; x2*=stretch_factor; if (!updated_area_valid) { updated_area.left = x1 ; updated_area.top = y1 ; updated_area.right = x2 ; updated_area.bottom = y2 ; } else { if (x1 < updated_area.left) updated_area.left = x1 ; if (y1 < updated_area.top) updated_area.top = y1 ; if (x2 > updated_area.right) updated_area.right = x2 ; if (y2 > updated_area.bottom) updated_area.bottom = y2; } updated_area_valid = TRUE; } void headerbar_click(int x) { if (x < bx_headerbar_entries) { bx_headerbar_entry[x].f(); } } void bx_win32_gui_c::mouse_enabled_changed_specific(bx_bool val) { if ((val != (bx_bool)mouseCaptureMode) && !mouseToggleReq) { mouseToggleReq = TRUE; mouseCaptureNew = val; } } void bx_win32_gui_c::get_capabilities(Bit16u *xres, Bit16u *yres, Bit16u *bpp) { if (desktop_y > 0) { *xres = desktop_x; *yres = desktop_y; *bpp = 32; } else { *xres = 1024; *yres = 768; *bpp = 32; } } void bx_win32_gui_c::set_tooltip(unsigned hbar_id, const char *tip) { bx_headerbar_entry[hbar_id].tooltip = tip; } #if BX_SHOW_IPS VOID CALLBACK MyTimer(HWND hwnd,UINT uMsg, UINT idEvent, DWORD dwTime) { bx_signal_handler(SIGALRM); } void bx_win32_gui_c::show_ips(Bit32u ips_count) { if (!ipsUpdate) { sprintf(ipsText, "IPS: %9u", ips_count); ipsUpdate = TRUE; } } #endif #if BX_USE_WINDOWS_FONTS void DrawChar (HDC hdc, unsigned char c, int xStart, int yStart, unsigned char cColor, int cs_start, int cs_end) { HDC hdcMem; POINT ptSize, ptOrg; HGDIOBJ oldObj; char str[2]; HFONT hFontOld; hdcMem = CreateCompatibleDC (hdc); SetMapMode (hdcMem, GetMapMode (hdc)); ptSize.x = xChar; ptSize.y = yChar; DPtoLP (hdc, &ptSize, 1); ptOrg.x = 0; ptOrg.y = 0; DPtoLP (hdcMem, &ptOrg, 1); oldObj = SelectObject(MemoryDC, MemoryBitmap); hFontOld=(HFONT)SelectObject(MemoryDC, hFont[FontId]); COLORREF crFore = SetTextColor(MemoryDC, GetColorRef(cColor&0xf)); COLORREF crBack = SetBkColor(MemoryDC, GetColorRef((cColor>>4)&0xf)); str[0]=c; str[1]=0; int y = FontId == 2 ? 16 : 8; TextOut(MemoryDC, xStart, yStart, str, 1); if (cs_start <= cs_end && cs_start < y) { RECT rc; SetBkColor(MemoryDC, GetColorRef(cColor&0xf)); SetTextColor(MemoryDC, GetColorRef((cColor>>4)&0xf)); rc.left = xStart+0; rc.right = xStart+xChar; if (cs_end >= y) cs_end = y-1; rc.top = yStart+cs_start*yChar/y; rc.bottom = yStart+(cs_end+1)*yChar/y; ExtTextOut(MemoryDC, xStart, yStart, ETO_CLIPPED|ETO_OPAQUE, &rc, str, 1, NULL); } SetBkColor(MemoryDC, crBack); SetTextColor(MemoryDC, crFore); SelectObject(MemoryDC, hFontOld); SelectObject(MemoryDC, oldObj); updateUpdated(xStart, yStart, ptSize.x + xStart - 1, ptSize.y + yStart - 1); DeleteDC (hdcMem); } void InitFont(void) { LOGFONT lf; lf.lfWidth = 8; lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfWeight = FW_MEDIUM; lf.lfItalic = FALSE; lf.lfUnderline=FALSE; lf.lfStrikeOut=FALSE; lf.lfCharSet=OEM_CHARSET; lf.lfOutPrecision=OUT_DEFAULT_PRECIS; lf.lfClipPrecision=CLIP_DEFAULT_PRECIS; lf.lfQuality=DEFAULT_QUALITY; lf.lfPitchAndFamily=FIXED_PITCH | FF_DONTCARE; wsprintf(lf.lfFaceName, "Lucida Console"); for (int i=0; i < 3; i++) { lf.lfHeight = 12 + i * 2; hFont[i]=CreateFontIndirect(&lf); } } void DestroyFont(void) { for(int i = 0; i < 3; i++) { DeleteObject(hFont[i]); } } #endif /* if BX_USE_WINDOWS_FONTS */ #endif /* if BX_WITH_WIN32 */