/* * $Id: win32.c,v 1.39 2005/01/14 18:58:03 phil Exp $ * Win32 support for XY display simulator * Phil Budne * September 2003 * Revised by Douglas A. Gwyn, 05 Feb. 2004 */ /* * Copyright (c) 2003-2004, Philip L. Budne * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the names of the authors shall * not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization * from the authors. */ /* use a thread to handle windows messages; */ #define THREADS /* * BUGS: * Does not allow you to close display window; * would need to tear down both system, and system independent data. * * now tries to handle PAINT message, as yet untested!! */ #include #include #include #include "ws.h" #include "display.h" #ifndef PIX_SIZE #define PIX_SIZE 1 #endif #define APP_CLASS "XYAppClass" #define APP_MENU "XYAppMenu" /* ?? */ /* * light pen location * see ws.h for full description */ int ws_lp_x = -1; int ws_lp_y = -1; static HWND static_wh; static HINSTANCE static_inst; static int xpixels, ypixels; static const char *window_name; static HBRUSH white_brush; static HBRUSH black_brush; #ifdef SWITCH_CURSORS static HCURSOR cross, arrow; #endif static __inline int map_key(int k) { switch (k) { case 186: return ';'; /* VK_OEM_1? */ case 222: return '\''; /* VK_OEM_7? */ } return k; } static void keydown(int k) { display_keydown(map_key(k)); } static void keyup(int k) { display_keyup(map_key(k)); } /* * here on any button click, or if mouse dragged while a button down */ static void mousepos(DWORD lp) { int x, y; x = LOWORD(lp); y = HIWORD(lp); /* convert to display coordinates */ #if PIX_SIZE > 1 x /= PIX_SIZE; y /= PIX_SIZE; #endif y = ypixels - 1 - y; /* if window has been stretched, can get out of range bits!! */ if (x >= 0 && x < xpixels && y >= 0 && y < ypixels) { /* checked by display_add_point() */ ws_lp_x = x; ws_lp_y = y; } } /* thoingggg!! "message for you sir!!!" */ static LRESULT CALLBACK patsy(HWND wh, UINT msg, WPARAM wp, LPARAM lp) /* "WndProc" */ { /* printf("msg %d\n", msg); */ switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_MOUSEMOVE: if (wp & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)) { #ifdef SWITCH_CURSORS if (ws_lp_x == -1 && !display_tablet) SetCursor(cross); #endif mousepos(lp); } #ifdef SWITCH_CURSORS else if (ws_lp_x != -1 && !display_tablet) SetCursor(arrow); #endif break; /* return?? */ case WM_LBUTTONDOWN: display_lp_sw = 1; case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: #ifdef SWITCH_CURSORS if (!display_tablet) SetCursor(cross); #endif mousepos(lp); break; /* return?? */ case WM_LBUTTONUP: display_lp_sw = 0; case WM_MBUTTONUP: case WM_RBUTTONUP: #ifdef SWITCH_CURSORS if (!display_tablet) SetCursor(arrow); #endif ws_lp_x = ws_lp_y = -1; break; /* return?? */ case WM_KEYDOWN: keydown(wp); break; case WM_KEYUP: keyup(wp); break; case WM_PAINT: display_repaint(); break; /* return?? */ } return DefWindowProc(wh, msg, wp, lp); } int ws_poll(int *valp, int maxus) { #ifdef THREADS /* msgthread handles window events; just delay simulator */ if (maxus > 0) Sleep((maxus+999)/1000); #else MSG msg; DWORD start; int maxms = (maxus + 999) / 1000; for (start = GetTickCount(); GetTickCount() - start < maxms; Sleep(1)) { /* empty message queue without blocking */ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } #endif return 1; } /* called from non-threaded main program */ int ws_loop(void (*func)(void *), void *arg) { int val; while (ws_poll(&val, 0)) (*func)(arg); return val; } /* worker for display init */ static void ws_init2(void) { WNDCLASS wc; int h, w; #ifdef SWITCH_CURSORS if (!display_tablet) { arrow = LoadCursor(NULL, IDC_ARROW); cross = LoadCursor(NULL, IDC_CROSS); } #endif black_brush = GetStockObject(BLACK_BRUSH); white_brush = GetStockObject(WHITE_BRUSH); wc.lpszClassName = APP_CLASS; wc.lpfnWndProc = patsy; wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; /* also CS_NOCLOSE? CS_SAVEBITS? */ wc.hInstance = static_inst = GetModuleHandleA(0); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); #ifdef SWITCH_CURSORS wc.hCursor = NULL; #else wc.hCursor = display_tablet ? NULL : LoadCursor(NULL, IDC_CROSS); #endif wc.hbrBackground = black_brush; wc.lpszMenuName = APP_MENU; wc.cbClsExtra = 0; wc.cbWndExtra = 0; /* WNDCLASSEX/RegisterClassEx include hIconSm (small icon) */ RegisterClass(&wc); /* * WS_OVERLAPPEDWINDOW=> * WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, * WS_MINIMIZEBOX, WS_MAXIMIZEBOX * * WS_CHILD (no menu bar), WS_POPUP (mutually exclusive) */ /* empirical crocks to get entire screen; */ w = (xpixels*PIX_SIZE)+6; h = (ypixels*PIX_SIZE)+32; /* XXX -- above values work with XP; Phil had +10,+30 */ static_wh = CreateWindow(APP_CLASS, /* registered class name */ window_name, /* window name */ WS_OVERLAPPED, /* style */ CW_USEDEFAULT, CW_USEDEFAULT, /* X,Y */ w, h, NULL, /* HWND hWndParent */ NULL, /* HMENU hMenu */ static_inst, /* application instance */ NULL); /* lpParam */ ShowWindow(static_wh, SW_SHOW); UpdateWindow(static_wh); } #ifdef THREADS static volatile int init_done; static DWORD msgthread_id; static DWORD WINAPI msgthread(LPVOID arg) { MSG msg; ws_init2(); /* XXX use a mutex? */ init_done = 1; while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } static void ws_thread_init(void) { HANDLE th = CreateThread(NULL, /* sec. attr. */ 0, /* stack size */ msgthread, NULL, /* param */ 0, /* flags */ &msgthread_id); CloseHandle(th); /* XXX use a mutex; don't wait forever!! */ while (!init_done) Sleep(200); } #endif /* THREADS */ /* called from display layer on first display op */ int ws_init(const char *name, int xp, int yp, int colors, void *dptr) { xpixels = xp; ypixels = yp; window_name = name; #ifdef THREADS ws_thread_init(); #else ws_init2(); #endif return 1; /* XXX return errors!! */ } void ws_shutdown (void) { } void * ws_color_rgb(int r, int g, int b) { /* XXX check for failure??? try GetNearestColor??? */ return CreateSolidBrush(RGB(r/256, g/256, b/256)); } void * ws_color_black(void) { return black_brush; } void * ws_color_white(void) { return white_brush; } void ws_display_point(int x, int y, void *color) { HDC dc; RECT r; HBRUSH brush = color; if (x > xpixels || y > ypixels) return; y = ypixels - 1 - y; /* invert y, top left origin */ /* top left corner */ r.left = x*PIX_SIZE; r.top = y*PIX_SIZE; /* bottom right corner, non-inclusive */ r.right = (x+1)*PIX_SIZE; r.bottom = (y+1)*PIX_SIZE; if (brush == NULL) brush = black_brush; dc = GetDC(static_wh); FillRect(dc, &r, brush); ReleaseDC(static_wh, dc); } void ws_sync(void) { /* noop */ } void ws_beep(void) { #if 0 /* play SystemDefault sound; does not work over terminal service */ MessageBeep(MB_OK); #else /* works over terminal service? Plays default sound/beep on Win9x/ME */ Beep(440, 500); /* Hz, ms. */ #endif } unsigned long os_elapsed(void) { static int new; unsigned long ret; static DWORD t[2]; /* * only returns milliseconds, but Sleep() * only takes milliseconds. * * wraps after 49.7 days of uptime. * DWORD is an unsigned long, so this should be OK */ t[new] = GetTickCount(); if (t[!new] == 0) ret = ~0L; /* +INF */ else ret = (t[new] - t[!new]) * 1000; new = !new; /* Ecclesiastes III */ return ret; }