blob: 3eb26802e74516c5f0540666009d159f0f3688c7 [file] [log] [blame] [raw]
/*
* $Id: win32.c,v 1.38 2004/02/07 06:32:01 phil Exp $
* Win32 support for XY display simulator
* Phil Budne <phil@ultimate.com>
* 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 <windows.h>
#include <stdio.h>
#include <stdlib.h>
#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 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)
;
}
#endif /* THREADS */
/* called from display layer on first display op */
int
ws_init(char *name, int xp, int yp, int colors)
{
xpixels = xp;
ypixels = yp;
window_name = name;
#ifdef THREADS
ws_thread_init();
#else
ws_init2();
#endif
return 1; /* XXX return errors!! */
}
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;
}