/** * 2ine; an OS/2 emulator for Linux. * * Please see the file LICENSE.txt in the source's root directory. * * This file written by Ryan C. Gordon. */ #include "os2native.h" #include "pmwin.h" #include "SDL.h" #include "pmwin-lx.h" // NOTE: PM reference manual says OS/2 generally ignores the HAB you pass // to functions, instead getting that info from the current thread, but // other IBM platforms might not do that, so programs should always pass // a valid HAB to everything. Things like WinCreateWindow() probably rely // on the current thread's HAB without requiring the API call to specify // it in any case. // So current approach: keep a pointer to the anchor block in the TIB, and // the anchor block keeps a copy of it's HAB. When the anchor block is // needed, we pull it from the TIB directly, and if it's in a function // where a HAB is specified, we fail if the handles don't match. // If I later find out that OS/2 doesn't even care even if you give it a // bogus HAB, we can relax this check. // !!! FIXME: You can send messages to HWNDs on a different thread, which // !!! FIXME: means HWND values must be unique across threads. Technically, // !!! FIXME: they have to be unique across processes, too. :/ #define FIRST_HWND_VALUE 10 #define FIRST_HPS_VALUE 10 // This is the keyname we use on an heavyweight SDL window to find our // associated Window*. #define WINDATA_WINDOWPTR_NAME "2ine_windowptr" static SDL_atomic_t GHABCounter; static SDL_atomic_t GHMQCounter; typedef struct WindowClass { char *name; PFNWP window_proc; ULONG style; ULONG data_len; struct WindowClass *next; } WindowClass; // these get shared with all children, but they're owned by the topmost parent. typedef struct { SDL_Window *window; SDL_Renderer *renderer; SDL_Texture *texture; } HeavyWeightWindow; typedef struct Window { PFNWP window_proc; HeavyWeightWindow heavy; struct Window *parent; struct Window *children; struct Window *sibling; struct Window *owner; const char *window_class_name; // Class at creation time. styles and window procs might change later, though! This is just to make debugging easier. char *text; void *data; size_t data_len; ULONG id; ULONG class_style; ULONG style; HWND hwnd; LONG x; LONG y; LONG w; LONG h; BOOL enabled; } Window; typedef struct MessageQueueItem { QMSG qmsg; struct MessageQueueItem *next; } MessageQueueItem; typedef struct { HMQ hmq; MessageQueueItem *head; MessageQueueItem *tail; MessageQueueItem *free_pool; } MessageQueue; typedef struct { HPS hps; Window *window; // !!! FIXME: this is gonna be WAY more complicated. } PresentationSpace; typedef struct { HAB hab; // actual HAB value for this block, for comparison. ERRORID last_error; WindowClass *registered_classes; MessageQueue message_queue; Window *windows; size_t windows_array_len; PresentationSpace *pres_spaces; size_t pres_spaces_array_len; } AnchorBlock; #define SET_WIN_ERROR_AND_RETURN(anchor, err, rc) { anchor->last_error = (err); return (rc); } static Window desktop_window; // !!! FIXME: initialize and upkeep on this. static inline AnchorBlock *getAnchorBlockNoHAB(void) { return (AnchorBlock *) LX_GETPOSTTIB()->anchor_block; } // getAnchorBlockNoHAB static AnchorBlock *getAnchorBlock(const HAB hab) { AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { return NULL; } else if (anchor->hab != hab) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HAB, NULL); } return anchor; } // getAnchorBlock static Window *getWindowFromHWND(const AnchorBlock *anchor, const HWND hwnd) { if (hwnd >= FIRST_HWND_VALUE) { const size_t idx = (size_t) (hwnd - FIRST_HWND_VALUE); if (idx >= anchor->windows_array_len) { return NULL; } Window *win = &anchor->windows[idx]; return (win->hwnd == hwnd) ? win : NULL; } else if (hwnd == HWND_DESKTOP) { return &desktop_window; } return NULL; } // getWindowFromHWND static PresentationSpace *getPresentationSpaceFromHPS(const AnchorBlock *anchor, const HPS hps) { if (hps >= FIRST_HPS_VALUE) { const size_t idx = (size_t) (hps - FIRST_HPS_VALUE); if (idx >= anchor->pres_spaces_array_len) { return NULL; } PresentationSpace *ps = &anchor->pres_spaces[idx]; return (ps->hps == hps) ? ps : NULL; } return NULL; } // getPresentationSpaceFromHPS static const char *messageName(const ULONG msg) { switch (msg) { #define MSGCASE(m) case m: return #m MSGCASE(WM_NULL); MSGCASE(WM_CREATE); MSGCASE(WM_DESTROY); MSGCASE(WM_ENABLE); MSGCASE(WM_SHOW); MSGCASE(WM_MOVE); MSGCASE(WM_SIZE); MSGCASE(WM_ADJUSTWINDOWPOS); MSGCASE(WM_CALCVALIDRECTS); MSGCASE(WM_SETWINDOWPARAMS); MSGCASE(WM_QUERYWINDOWPARAMS); MSGCASE(WM_HITTEST); MSGCASE(WM_ACTIVATE); MSGCASE(WM_SETFOCUS); MSGCASE(WM_SETSELECTION); MSGCASE(WM_PPAINT); MSGCASE(WM_PSETFOCUS); MSGCASE(WM_PSYSCOLORCHANGE); MSGCASE(WM_PSIZE); MSGCASE(WM_PACTIVATE); MSGCASE(WM_PCONTROL); MSGCASE(WM_COMMAND); MSGCASE(WM_SYSCOMMAND); MSGCASE(WM_HELP); MSGCASE(WM_PAINT); MSGCASE(WM_TIMER); MSGCASE(WM_SEM1); MSGCASE(WM_SEM2); MSGCASE(WM_SEM3); MSGCASE(WM_SEM4); MSGCASE(WM_CLOSE); MSGCASE(WM_QUIT); MSGCASE(WM_SYSCOLORCHANGE); MSGCASE(WM_SYSVALUECHANGED); MSGCASE(WM_APPTERMINATENOTIFY); MSGCASE(WM_PRESPARAMCHANGED); MSGCASE(WM_CONTROL); MSGCASE(WM_VSCROLL); MSGCASE(WM_HSCROLL); MSGCASE(WM_INITMENU); MSGCASE(WM_MENUSELECT); MSGCASE(WM_MENUEND); MSGCASE(WM_DRAWITEM); MSGCASE(WM_MEASUREITEM); MSGCASE(WM_CONTROLPOINTER); MSGCASE(WM_QUERYDLGCODE); MSGCASE(WM_INITDLG); MSGCASE(WM_SUBSTITUTESTRING); MSGCASE(WM_MATCHMNEMONIC); MSGCASE(WM_SAVEAPPLICATION); MSGCASE(WM_FLASHWINDOW); MSGCASE(WM_FORMATFRAME); MSGCASE(WM_UPDATEFRAME); MSGCASE(WM_FOCUSCHANGE); MSGCASE(WM_SETBORDERSIZE); MSGCASE(WM_TRACKFRAME); MSGCASE(WM_MINMAXFRAME); MSGCASE(WM_SETICON); MSGCASE(WM_QUERYICON); MSGCASE(WM_SETACCELTABLE); MSGCASE(WM_QUERYACCELTABLE); MSGCASE(WM_TRANSLATEACCEL); MSGCASE(WM_QUERYTRACKINFO); MSGCASE(WM_QUERYBORDERSIZE); MSGCASE(WM_NEXTMENU); MSGCASE(WM_ERASEBACKGROUND); MSGCASE(WM_QUERYFRAMEINFO); MSGCASE(WM_QUERYFOCUSCHAIN); MSGCASE(WM_OWNERPOSCHANGE); MSGCASE(WM_CALCFRAMERECT); MSGCASE(WM_WINDOWPOSCHANGED); MSGCASE(WM_ADJUSTFRAMEPOS); MSGCASE(WM_QUERYFRAMECTLCOUNT); MSGCASE(WM_QUERYHELPINFO); MSGCASE(WM_SETHELPINFO); MSGCASE(WM_ERROR); MSGCASE(WM_REALIZEPALETTE); MSGCASE(WM_RENDERFMT); MSGCASE(WM_RENDERALLFMTS); MSGCASE(WM_DESTROYCLIPBOARD); MSGCASE(WM_PAINTCLIPBOARD); MSGCASE(WM_SIZECLIPBOARD); MSGCASE(WM_HSCROLLCLIPBOARD); MSGCASE(WM_VSCROLLCLIPBOARD); MSGCASE(WM_DRAWCLIPBOARD); MSGCASE(WM_MOUSEMOVE); MSGCASE(WM_BUTTON1DOWN); MSGCASE(WM_BUTTON1UP); MSGCASE(WM_BUTTON1DBLCLK); MSGCASE(WM_BUTTON2DOWN); MSGCASE(WM_BUTTON2UP); MSGCASE(WM_BUTTON2DBLCLK); MSGCASE(WM_BUTTON3DOWN); MSGCASE(WM_BUTTON3UP); MSGCASE(WM_BUTTON3DBLCLK); MSGCASE(WM_CHAR); MSGCASE(WM_VIOCHAR); MSGCASE(WM_JOURNALNOTIFY); MSGCASE(WM_MOUSEMAP); MSGCASE(WM_VRNDISABLED); MSGCASE(WM_VRNENABLED); MSGCASE(WM_DDE_INITIATE); MSGCASE(WM_DDE_REQUEST); MSGCASE(WM_DDE_ACK); MSGCASE(WM_DDE_DATA); MSGCASE(WM_DDE_ADVISE); MSGCASE(WM_DDE_UNADVISE); MSGCASE(WM_DDE_POKE); MSGCASE(WM_DDE_EXECUTE); MSGCASE(WM_DDE_TERMINATE); MSGCASE(WM_DDE_INITIATEACK); MSGCASE(WM_QUERYCONVERTPOS); MSGCASE(WM_MSGBOXINIT); MSGCASE(WM_MSGBOXDISMISS); MSGCASE(WM_CTLCOLORCHANGE); MSGCASE(WM_QUERYCTLTYPE); MSGCASE(WM_CHORD); MSGCASE(WM_BUTTON1MOTIONSTART); MSGCASE(WM_BUTTON1MOTIONEND); MSGCASE(WM_BUTTON1CLICK); MSGCASE(WM_BUTTON2MOTIONSTART); MSGCASE(WM_BUTTON2MOTIONEND); MSGCASE(WM_BUTTON2CLICK); MSGCASE(WM_BUTTON3MOTIONSTART); MSGCASE(WM_BUTTON3MOTIONEND); MSGCASE(WM_BUTTON3CLICK); MSGCASE(WM_BEGINDRAG); MSGCASE(WM_ENDDRAG); MSGCASE(WM_SINGLESELECT); MSGCASE(WM_OPEN); MSGCASE(WM_CONTEXTMENU); MSGCASE(WM_CONTEXTHELP); MSGCASE(WM_TEXTEDIT); MSGCASE(WM_BEGINSELECT); MSGCASE(WM_ENDSELECT); MSGCASE(WM_PICKUP); MSGCASE(WM_SEMANTICEVENT); MSGCASE(WM_USER); #undef MSGCASE default: break; } return "???"; } // messageName static WindowClass *findRegisteredClass(AnchorBlock *anchor, const char *classname) { const ULONG intclass = (ULONG) (size_t) classname; if ((intclass >= 0xffff0001) && (intclass <= 0xffff0070)) { // built-in class, like WC_BUTTON or whatever. switch (intclass) { // !!! FIXME: write me. case WC_FRAME: case WC_COMBOBOX: case WC_BUTTON: case WC_MENU: case WC_STATIC: case WC_ENTRYFIELD: case WC_LISTBOX: case WC_SCROLLBAR: case WC_TITLEBAR: case WC_MLE: case WC_APPSTAT: case WC_KBDSTAT: case WC_PECIC: case WC_DBE_KKPOPUP: case WC_SPINBUTTON: case WC_CONTAINER: case WC_SLIDER: case WC_VALUESET: case WC_NOTEBOOK: case WC_CIRCULARSLIDER: default: return NULL; } } else { for (WindowClass *i = anchor->registered_classes; i; i = i->next) { if (strcmp(i->name, classname) == 0) { return i; } } } return NULL; } // findRegisteredClass static ULONG currentSystemTicks(void) { FIXME("this isn't correct"); // see notes in processSDLEvent() for details. return SDL_GetTicks(); } // currentSystemTicks HAB WinInitialize(ULONG flOptions) { TRACE_NATIVE("WinInitialize(%u)", (uint) flOptions); if (flOptions != 0) { return NULLHANDLE; // reserved; must be zero. } LxPostTIB *posttib = LX_GETPOSTTIB(); if (posttib->anchor_block != NULL) { return NULLHANDLE; // fail if thread already has a HAB } if (!SDL_WasInit(SDL_INIT_VIDEO)) { if (SDL_Init(SDL_INIT_VIDEO) == -1) { fprintf(stderr, "SDL_INIT_VIDEO failed: %s\n", SDL_GetError()); return NULLHANDLE; } } FIXME("OS/2 doesn't support multi-monitor"); // this might not be true, actually. SDL_Rect desktoprect; SDL_GetDisplayBounds(0, &desktoprect); desktop_window.hwnd = HWND_DESKTOP; desktop_window.w = desktoprect.w; desktop_window.h = desktoprect.h; AnchorBlock *anchor = calloc(1, sizeof (AnchorBlock)); if (anchor == NULL) { return NULLHANDLE; } anchor->windows_array_len = 16; anchor->windows = (Window *) malloc(sizeof (Window) * anchor->windows_array_len); if (!anchor->windows) { free(anchor); return NULLHANDLE; } memset(anchor->windows, '\0', sizeof (Window) * anchor->windows_array_len); anchor->pres_spaces_array_len = 16; anchor->pres_spaces = (PresentationSpace *) malloc(sizeof (PresentationSpace) * anchor->pres_spaces_array_len); if (!anchor->pres_spaces) { free(anchor->windows); free(anchor); return NULLHANDLE; } memset(anchor->pres_spaces, '\0', sizeof (PresentationSpace) * anchor->pres_spaces_array_len); const int ihab = SDL_AtomicAdd(&GHABCounter, 1) + 1; if (ihab <= 0) { // this is clearly a pathological program. free(anchor->pres_spaces); free(anchor->windows); free(anchor); return NULLHANDLE; } const HAB hab = (HAB) ihab; anchor->hab = hab; posttib->anchor_block = anchor; return hab; } // WinInitialize static MRESULT sendMessage(Window *win, ULONG msg, MPARAM mp1, MPARAM mp2) { PFNWP winproc = win->window_proc; if (!winproc) { winproc = WinDefWindowProc; } TRACE_EVENT("EVENT SEND { hwnd=%u, msg=%u (%s), mp1=%p, mp2=%p, proc=%p }", (uint) win->hwnd, (uint) msg, messageName(msg), mp1, mp2, winproc); return winproc(win->hwnd, msg, mp1, mp2); } // sendMessage static BOOL destroyMessageQueue(AnchorBlock *anchor) { MessageQueueItem *next = NULL; for (MessageQueueItem *i = anchor->message_queue.head; i; i = next) { next = i->next; free(i); } for (MessageQueueItem *i = anchor->message_queue.free_pool; i; i = next) { next = i->next; free(i); } TRACE_EVENT("HMQ %u destroyed", (uint) anchor->message_queue.hmq); memset(&anchor->message_queue, '\0', sizeof (MessageQueue)); return TRUE; } // destroyMessageQueue static void destroyWindow(AnchorBlock *anchor, Window *win) { if (!win || (win->hwnd == NULLHANDLE)) { return; } // !!! FIXME: hide window first, and optionally send WM_ACTIVATE message if losing focus. // !!! FIXME: send WM_RENDERALLFMTS if we are clipboard owner and unrendered formats are in the clipboard. // send WM_DESTROY. This is a simple notification, and cannot prevent destruction. sendMessage(win, WM_DESTROY, 0, 0); // Next, destroy all child windows. Window *next = NULL; for (Window *i = win->children; i != NULL; i = next) { next = i->sibling; destroyWindow(anchor, i); } // !!! FIXME: see notes in SDK reference about disassociating/destroying HPS objects. // if this is a heavyweight window, destroy those parts. if (win->parent == &desktop_window) { SDL_DestroyTexture(win->heavy.texture); SDL_DestroyRenderer(win->heavy.renderer); SDL_DestroyWindow(win->heavy.window); } if (win->parent->children == win) { win->parent->children = win->sibling; } else { for (Window *i = win->parent->children; i != NULL; i = i->sibling) { assert(i != win); if (i->sibling == win) { i->sibling = win->sibling; break; } } } free(win->text); free(win->data); memset(win, '\0', sizeof (*win)); } // destroyWindow static void destroyPresentationSpace(AnchorBlock *anchor, PresentationSpace *ps) { if (!ps || (ps->hps == NULLHANDLE)) { return; } FIXME("this will be more complicated later"); memset(ps, '\0', sizeof (*ps)); } // destroyPresentationSpace BOOL WinTerminate(HAB hab) { TRACE_NATIVE("WinTerminate(%u)", (uint) hab); AnchorBlock *anchor = getAnchorBlock(hab); if (!anchor) { return FALSE; } for (size_t i = 0; i < anchor->pres_spaces_array_len; i++) { destroyPresentationSpace(anchor, &anchor->pres_spaces[i]); } free(anchor->pres_spaces); for (size_t i = 0; i < anchor->windows_array_len; i++) { destroyWindow(anchor, &anchor->windows[i]); } free(anchor->windows); WindowClass *winclassnext = NULL; for (WindowClass *i = anchor->registered_classes; i; i = winclassnext) { winclassnext = i->next; free(i->name); free(i); } if (anchor->message_queue.hmq != NULLHANDLE) { destroyMessageQueue(anchor); } free(anchor); LX_GETPOSTTIB()->anchor_block = NULL; SDL_Quit(); // !!! FIXME: does this reference count? return TRUE; } // WinTerminate ERRORID WinGetLastError(HAB hab) { TRACE_NATIVE("WinGetLastError(%u)", (uint) hab); AnchorBlock *anchor = getAnchorBlock(hab); if (!anchor) { return PMERR_INVALID_HAB; } const ERRORID retval = anchor->last_error; SET_WIN_ERROR_AND_RETURN(anchor, NO_ERROR, retval); } // WinGetLastError HMQ WinCreateMsgQueue(HAB hab, LONG cmsg) { TRACE_NATIVE("WinCreateMsgQueue(%u, %d)", (uint) hab, (int) cmsg); AnchorBlock *anchor = getAnchorBlock(hab); if (!anchor) { return NULLHANDLE; } else if (anchor->message_queue.hmq != NULLHANDLE) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_MSG_QUEUE_ALREADY_EXISTS, NULLHANDLE); } // as of OS/2 Warp 4.0, cmsg is ignored (the queue is always dynamically allocated), so we ignore it too. assert(anchor->message_queue.head == NULL); assert(anchor->message_queue.tail == NULL); assert(anchor->message_queue.free_pool == NULL); const int ihmq = SDL_AtomicAdd(&GHMQCounter, 1) + 1; if (ihmq <= 0) { // this is clearly a pathological program. return NULLHANDLE; } const HMQ hmq = (HMQ) ihmq; anchor->message_queue.hmq = hmq; TRACE_EVENT("HMQ %u created", (uint) hmq); return hmq; } // WinCreateMsgQueue static ERRORID postMessage(AnchorBlock *anchor, const QMSG *qmsg) { TRACE_EVENT("EVENT POST { hwnd=%u, msg=%u (%s), mp1=%p, mp2=%p, time=%u, ptl={%d,%d}, reserved=%u }", (uint) qmsg->hwnd, (uint) qmsg->msg, messageName(qmsg->msg), qmsg->mp1, qmsg->mp2, (uint) qmsg->time, (int) qmsg->ptl.x, (int) qmsg->ptl.y, (uint) qmsg->reserved); MessageQueueItem *item = anchor->message_queue.free_pool; if (item) { anchor->message_queue.free_pool = item->next; } else { item = (MessageQueueItem *) malloc(sizeof (*item)); if (item == NULL) { return FALSE; } } memcpy(&item->qmsg, qmsg, sizeof (*qmsg)); item->next = NULL; assert(!anchor->message_queue.head == !anchor->message_queue.tail); if (anchor->message_queue.tail) { anchor->message_queue.tail->next = item; } else { anchor->message_queue.head = item; } anchor->message_queue.tail = item; return TRUE; } // postMessage static inline Window *getWindowFromSDLWindow(SDL_Window *sdlwin) { return (Window *) sdlwin ? SDL_GetWindowData(sdlwin, WINDATA_WINDOWPTR_NAME) : NULL; } // getWindowFromSDLWindow #if 0 static Window *hittestWindow(SDL_Window *sdlwin, int x, int y) { if (!sdlwin) { return NULL; } Window *win = getWindowFromSDLWindow(sdlwin); if (!win) { return NULL; } // (win) is currently the oldest parent, which is associated with the // SDL_Window. We need to see which lightweight child window, if any, // this event was meant for. // y coordinates are flipped on OS/2. zero is the bottom of the window. y = sdlwin->h - y; // !!! FIXME WRITE ME for (Window *i = if (win->class_style & CS_HITTEST) { sendMessage(win, WM_HITTEST, } } // findWindowFromSDL #endif static BOOL postTimestampedMsg(AnchorBlock *anchor, HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2, ULONG ticks) { const QMSG qmsg = { hwnd, msg, mp1, mp2, currentSystemTicks(), { 0, 0 }, 0 }; return postMessage(anchor, &qmsg); } // postTimestampedMsg #if 0 static BOOL postSimpleMsg(AnchorBlock *anchor, HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { return postTimestampedMsg(anchor, hwnd, msg, mp1, mp2, currentSystemTicks()); } // postSimpleMsg #endif static BOOL postExposedMessages(AnchorBlock *anchor, Window *win, const ULONG ticks) { FIXME("this should WinInvalidateRect() the windows, not send paint messages"); FIXME("this is a recursive hell"); // we need to start at the end of the sibling list, which is the bottom of the stack. BOOL retval = FALSE; if (win) { retval |= postExposedMessages(anchor, win->sibling, ticks); if (win->style & WS_VISIBLE) { FIXME("for CS_SYNCPAINT classes, this should send the WM_PAINT message right now instead of posting"); retval |= postTimestampedMsg(anchor, win->hwnd, WM_PAINT, 0, 0, ticks); retval |= postExposedMessages(anchor, win->children, ticks); } } return retval; } // postExposedMessages static const char *sdlEventName(const SDL_EventType ev) { switch (ev) { #define EVCASE(m) case m: return #m EVCASE(SDL_QUIT); EVCASE(SDL_APP_TERMINATING); EVCASE(SDL_APP_LOWMEMORY); EVCASE(SDL_APP_WILLENTERBACKGROUND); EVCASE(SDL_APP_DIDENTERBACKGROUND); EVCASE(SDL_APP_WILLENTERFOREGROUND); EVCASE(SDL_APP_DIDENTERFOREGROUND); EVCASE(SDL_WINDOWEVENT); EVCASE(SDL_SYSWMEVENT); EVCASE(SDL_KEYDOWN); EVCASE(SDL_KEYUP); EVCASE(SDL_TEXTEDITING); EVCASE(SDL_TEXTINPUT); EVCASE(SDL_KEYMAPCHANGED); EVCASE(SDL_MOUSEMOTION); EVCASE(SDL_MOUSEBUTTONDOWN); EVCASE(SDL_MOUSEBUTTONUP); EVCASE(SDL_MOUSEWHEEL); EVCASE(SDL_JOYAXISMOTION); EVCASE(SDL_JOYBALLMOTION); EVCASE(SDL_JOYHATMOTION); EVCASE(SDL_JOYBUTTONDOWN); EVCASE(SDL_JOYBUTTONUP); EVCASE(SDL_JOYDEVICEADDED); EVCASE(SDL_JOYDEVICEREMOVED); EVCASE(SDL_CONTROLLERAXISMOTION); EVCASE(SDL_CONTROLLERBUTTONDOWN); EVCASE(SDL_CONTROLLERBUTTONUP); EVCASE(SDL_CONTROLLERDEVICEADDED); EVCASE(SDL_CONTROLLERDEVICEREMOVED); EVCASE(SDL_CONTROLLERDEVICEREMAPPED); EVCASE(SDL_FINGERDOWN); EVCASE(SDL_FINGERUP); EVCASE(SDL_FINGERMOTION); EVCASE(SDL_DOLLARGESTURE); EVCASE(SDL_DOLLARRECORD); EVCASE(SDL_MULTIGESTURE); EVCASE(SDL_CLIPBOARDUPDATE); EVCASE(SDL_DROPFILE); EVCASE(SDL_DROPTEXT); EVCASE(SDL_DROPBEGIN); EVCASE(SDL_DROPCOMPLETE); EVCASE(SDL_AUDIODEVICEADDED); EVCASE(SDL_AUDIODEVICEREMOVED); EVCASE(SDL_RENDER_TARGETS_RESET); EVCASE(SDL_RENDER_DEVICE_RESET); EVCASE(SDL_USEREVENT); #undef EVCASE default: break; } return "???"; } // sdlEventName static const char *sdlWindowEventName(const SDL_WindowEventID ev) { switch (ev) { #define EVCASE(m) case m: return #m EVCASE(SDL_WINDOWEVENT_SHOWN); EVCASE(SDL_WINDOWEVENT_HIDDEN); EVCASE(SDL_WINDOWEVENT_EXPOSED); EVCASE(SDL_WINDOWEVENT_MOVED); EVCASE(SDL_WINDOWEVENT_RESIZED); EVCASE(SDL_WINDOWEVENT_SIZE_CHANGED); EVCASE(SDL_WINDOWEVENT_MINIMIZED); EVCASE(SDL_WINDOWEVENT_MAXIMIZED); EVCASE(SDL_WINDOWEVENT_RESTORED); EVCASE(SDL_WINDOWEVENT_ENTER); EVCASE(SDL_WINDOWEVENT_LEAVE); EVCASE(SDL_WINDOWEVENT_FOCUS_GAINED); EVCASE(SDL_WINDOWEVENT_FOCUS_LOST); EVCASE(SDL_WINDOWEVENT_CLOSE); EVCASE(SDL_WINDOWEVENT_TAKE_FOCUS); EVCASE(SDL_WINDOWEVENT_HIT_TEST); #undef EVCASE default: break; } return "???"; } // sdlWindowEventName static BOOL processSDLEvent(AnchorBlock *anchor, const SDL_Event *sdlevent) { // !!! FIXME: we just drop events if we run out of memory, etc. // !!! FIXME: QMSG::time appears to be milliseconds since the system // !!! FIXME: booted (presumably rolling over every 38 days). SDL's // !!! FIXME: event timestamps (and SDL_GetTicks()) are milliseconds // !!! FIXME: since SDL_Init(), which is more or less since app startup. // !!! FIXME: We might need a way to map these, so they're consistent // !!! FIXME: across processes. // !!! FIXME: Also, it's well known that SDL event timestamps are when // !!! FIXME: SDL gets the event from the OS, not the timestamp of when // !!! FIXME: the OS generated it, which might be a problem. /* sdfsdf HWND hwnd; ULONG msg; MPARAM mp1; MPARAM mp2; ULONG time; POINTL ptl; ULONG reserved; sdfsdf */ //SDL_GetWindowFromID(window) sdfsdf const ULONG ticks = (ULONG) sdlevent->common.timestamp; TRACE_EVENT("EVENT SDL { type=0x%X (%s) }", (uint) sdlevent->type, sdlEventName(sdlevent->type)); switch (sdlevent->type) { case SDL_QUIT: return postTimestampedMsg(anchor, NULLHANDLE, WM_QUIT, 0, 0, ticks); case SDL_WINDOWEVENT: TRACE_EVENT("EVENT SDL WINDOW { type=0x%X (%s) }", (uint) sdlevent->window.event, sdlWindowEventName(sdlevent->window.event)); switch (sdlevent->window.event) { case SDL_WINDOWEVENT_SHOWN: case SDL_WINDOWEVENT_EXPOSED: { // maybe this doesn't matter in a world of compositing window managers, but it would be nice to completely avoid sending WM_PAINT to child windows that otherwise don't apply. // This only applies for heavyweight windows the OS is marking as exposed, not OS/2 apps marking OS/2 HWNDs as invalid for various reasons. FIXME("SDL doesn't report invalid subregions, just that a window needs repainting"); Window *win = getWindowFromSDLWindow(SDL_GetWindowFromID(sdlevent->window.windowID)); if (!win) { return FALSE; } return postExposedMessages(anchor, win, ticks); } default: break; // not (currently) supported. } return FALSE; #if 0 case SDL_WINDOWEVENT_SHOWN: case SDL_WINDOWEVENT_HIDDEN: case SDL_WINDOWEVENT_MOVED, /**< Window has been moved to data1, data2 */ case SDL_WINDOWEVENT_RESIZED, /**< Window has been resized to data1xdata2 */ case SDL_WINDOWEVENT_SIZE_CHANGED, /**< The window size has changed, either as a result of an API call or through the system or user changing the window size. */ case SDL_WINDOWEVENT_MINIMIZED, /**< Window has been minimized */ case SDL_WINDOWEVENT_MAXIMIZED, /**< Window has been maximized */ case SDL_WINDOWEVENT_RESTORED, /**< Window has been restored to normal size and position */ case SDL_WINDOWEVENT_ENTER, /**< Window has gained mouse focus */ case SDL_WINDOWEVENT_LEAVE, /**< Window has lost mouse focus */ case SDL_WINDOWEVENT_FOCUS_GAINED, /**< Window has gained keyboard focus */ case SDL_WINDOWEVENT_FOCUS_LOST, /**< Window has lost keyboard focus */ case SDL_WINDOWEVENT_CLOSE, /**< The window manager requests that the window be closed */ case SDL_WINDOWEVENT_TAKE_FOCUS, /**< Window is being offered a focus (should SetWindowInputFocus() on itself or a subwindow, or ignore) */ //case SDL_WINDOWEVENT_HIT_TEST /**< Window had a hit test that wasn't SDL_HITTEST_NORMAL. */ // SDL_MOUSEMOTION = 0x400, /**< Mouse moved */ // SDL_MOUSEBUTTONDOWN, /**< Mouse button pressed */ // SDL_MOUSEBUTTONUP, /**< Mouse button released */ // SDL_MOUSEWHEEL, /**< Mouse wheel motion */ // SDL_KEYDOWN = 0x300, /**< Key pressed */ // SDL_KEYUP, /**< Key released */ // SDL_TEXTINPUT, /**< Keyboard text input */ // SDL_CLIPBOARDUPDATE = 0x900, /**< The clipboard changed */ // SDL_DROPFILE = 0x1000, /**< The system requests a file open */ // SDL_DROPTEXT, /**< text/plain drag-and-drop event */ // SDL_DROPBEGIN, /**< A new set of drops is beginning (NULL filename) */ // SDL_DROPCOMPLETE, /**< Current set of drops is now complete (NULL filename) */ case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: if (sdlevent->button.clicks == 1) {} #endif default: return FALSE; // not (currently) supported. } assert(!"shouldn't hit this"); return TRUE; } // processSDLEvent static void pumpEvents(AnchorBlock *anchor) { SDL_Event sdlevent; BOOL queued = FALSE; // run through anything pending. If we don't post anything to the OS/2 // event queue, we need to block until we do, though, so then we'll use // SDL_WaitEvent() in hopes that someday it's fully waitable. while (SDL_PollEvent(&sdlevent)) { queued |= processSDLEvent(anchor, &sdlevent); } while (!queued) { // block until stuff shows up. while (!SDL_WaitEvent(&sdlevent)) { SDL_Delay(10); // dunno what else to do here... } queued |= processSDLEvent(anchor, &sdlevent); while (SDL_PollEvent(&sdlevent)) { // catch anything else pending. queued |= processSDLEvent(anchor, &sdlevent); } } } // waitForMessage static BOOL skipMessage(const PQMSG qmsg, HWND hwndFilter, ULONG msgFilterFirst, ULONG msgFilterLast) { if (hwndFilter && (qmsg->hwnd != hwndFilter)) { return TRUE; } else if (msgFilterFirst || msgFilterLast) { const ULONG msg = qmsg->msg; if (msgFilterFirst > msgFilterLast) { // reject if between first and last. if ((msg >= msgFilterLast) && (msg <= msgFilterFirst)) return TRUE; } else { // reject if not between first and last. if ((msg < msgFilterFirst) || (msg > msgFilterLast)) return TRUE; } } return FALSE; } // skipMessage BOOL WinGetMsg(HAB hab, PQMSG pqmsg, HWND hwndFilter, ULONG msgFilterFirst, ULONG msgFilterLast) { TRACE_NATIVE("WinGetMsg(%u, %p, %u, %u, %u)", (uint) hab, pqmsg, (uint) hwndFilter, (uint) msgFilterFirst, (uint) msgFilterLast); const BOOL bIsFiltering = (hwndFilter || msgFilterFirst || msgFilterLast); AnchorBlock *anchor = getAnchorBlock(hab); if (!anchor) { return FALSE; } else if (hwndFilter) { FIXME("fail with PMERR_INVALID_HWND if this is a bogus HWND."); } while (1) { MessageQueueItem *prev = NULL; MessageQueueItem *i = anchor->message_queue.head; if (bIsFiltering) { for (; i != NULL; prev = i, i = i->next) { if (!skipMessage(&i->qmsg, hwndFilter, msgFilterFirst, msgFilterLast)) { break; } } } // Found an event? Remove from queue, return that info. if (i != NULL) { memcpy(pqmsg, &i->qmsg, sizeof (QMSG)); TRACE_EVENT("EVENT GET { hwnd=%u, msg=%u (%s), mp1=%p, mp2=%p, time=%u, ptl={%d,%d}, reserved=%u }", (uint) pqmsg->hwnd, (uint) pqmsg->msg, messageName(pqmsg->msg), pqmsg->mp1, pqmsg->mp2, (uint) pqmsg->time, (int) pqmsg->ptl.x, (int) pqmsg->ptl.y, (uint) pqmsg->reserved); if (prev) { prev->next = i->next; } else { anchor->message_queue.head = i->next; } if (!anchor->message_queue.head) { assert(anchor->message_queue.tail == i); anchor->message_queue.tail = NULL; } i->next = anchor->message_queue.free_pool; anchor->message_queue.free_pool = i; return (pqmsg->msg != WM_QUIT) ? TRUE : FALSE; } pumpEvents(anchor); // may block. } assert(!"shouldn't hit this."); return TRUE; // shouldn't hit this. } // WinGetMsg MRESULT WinDispatchMsg(HAB hab, PQMSG pqmsg) { TRACE_NATIVE("WinDispatchMsg(%u, %p)", (uint) hab, pqmsg); AnchorBlock *anchor = getAnchorBlock(hab); if (anchor) { Window *win = getWindowFromHWND(anchor, pqmsg->hwnd); if (win) { return sendMessage(win, pqmsg->msg, pqmsg->mp1, pqmsg->mp2); } } return 0; } // WinDispatchMsg BOOL WinDestroyMsgQueue(HMQ hmq) { TRACE_NATIVE("WinDestroyMsgQueue(%u)", (uint) hmq); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { return FALSE; } else if ((hmq == NULLHANDLE) || (anchor->message_queue.hmq != hmq)) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HMQ, FALSE); } return destroyMessageQueue(anchor); } // WinDestroyMsgQueue BOOL WinRegisterClass(HAB hab, PSZ pszClassName, PFNWP pfnWndProc, ULONG flStyle, ULONG cbWindowData) { TRACE_NATIVE("WinRegisterClass(%u, '%s', %p, %u, %u)", (uint) hab, pszClassName, pfnWndProc, (uint) flStyle, (uint) cbWindowData); AnchorBlock *anchor = getAnchorBlock(hab); if (!anchor) { return FALSE; } WindowClass *winclass = findRegisteredClass(anchor, pszClassName); if (!winclass) { // PM docs say this call can replace existing class. winclass = (WindowClass *) malloc(sizeof (WindowClass)); if (!winclass) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_HEAP_OUT_OF_MEMORY, FALSE); } else if ((winclass->name = strdup(pszClassName)) == NULL) { free(winclass); SET_WIN_ERROR_AND_RETURN(anchor, PMERR_HEAP_OUT_OF_MEMORY, FALSE); } winclass->next = anchor->registered_classes; anchor->registered_classes = winclass; } winclass->window_proc = pfnWndProc; winclass->style = flStyle; winclass->data_len = cbWindowData; return TRUE; } // WinRegisterClass MRESULT WinSendMsg(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { // !!! FIXME: This needs to be able to send to windows in different // !!! FIXME: threads (and processes!), so this will need some effort to // !!! FIXME: support. TRACE_NATIVE("WinSendMsg(%u, %u, %p, %p)", (uint) hwnd, (uint) msg, mp1, mp2); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (anchor) { Window *win = getWindowFromHWND(anchor, hwnd); if (win) { return sendMessage(win, msg, mp1, mp2); } } return (MRESULT) 0; } // WinSendMsg BOOL WinPostMsg(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { // !!! FIXME: SDL docs say you can call this, but not WinSendMsg() if // !!! FIXME: the thread doesn't have a message queue, but that has to // !!! FIXME: be a typo, right? How do you post a message to a queue that // !!! FIXME: doesn't exist? But one could call a window procedure // !!! FIXME: directly... // !!! FIXME: This needs to be able to post to windows in different // !!! FIXME: threads (and processes!), so this will need some effort to // !!! FIXME: support. TRACE_NATIVE("WinPostMsg(%u, %u, %p, %p)", (uint) hwnd, (uint) msg, mp1, mp2); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { return FALSE; } if (anchor->message_queue.hmq == NULLHANDLE) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_NO_MSG_QUEUE, FALSE); } Window *win = getWindowFromHWND(anchor, hwnd); if (!win) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, FALSE); } int mousex, mousey; SDL_GetGlobalMouseState(&mousex, &mousey); FIXME("track this ourselves?"); const QMSG qmsg = { hwnd, msg, mp1, mp2, currentSystemTicks(), { mousex, mousey }, 0 }; const ERRORID rc = postMessage(anchor, &qmsg); if (rc) { SET_WIN_ERROR_AND_RETURN(anchor, rc, FALSE); } return TRUE; } // WinPostMsg BOOL WinPostQueueMsg(HMQ hmq, ULONG msg, MPARAM mp1, MPARAM mp2) { TRACE_NATIVE("WinPostMsgQueue(%u, %u, %p, %p)", (uint) hmq, (uint) msg, mp1, mp2); // I assume this works like win32's PostThreadMessage(), in that there // isn't a target HWND, so the target thread would have to deal with this // message after a call to WinGetMsg() and not in a window procedure // during a call to WinDispatchMsg(). // (!!! FIXME: right now these are stored on the anchor block and not // anywhere global I can reach it.) FIXME("need to be able to access other threads' message queues"); return FALSE; } // WinPostQueueMsg HWND WinCreateWindow(HWND hwndParent, PSZ pszClass, PSZ pszName, ULONG flStyle, LONG x, LONG y, LONG cx, LONG cy, HWND hwndOwner, HWND hwndInsertBehind, ULONG id, PVOID pCtlData, PVOID pPresParams) { TRACE_NATIVE("WinCreateWindow(%u, '%s', '%s', %u, %d, %d, %d, %d, %u, %u, %u, %p, %p)", (uint) hwndParent, pszClass, pszName, (uint) flStyle, (int) x, (int) y, (int) cx, (int) cy, (uint) hwndOwner, hwndInsertBehind, (uint) id, pCtlData, pPresParams); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { return NULLHANDLE; } if (anchor->message_queue.hmq == NULLHANDLE) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_NO_MSG_QUEUE, NULLHANDLE); } const WindowClass *winclass = findRegisteredClass(anchor, pszClass); if (!winclass) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_PARM, NULLHANDLE); } Window *parent = getWindowFromHWND(anchor, hwndParent); if (!parent) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, NULLHANDLE); } Window *owner = NULL; if (hwndOwner != NULLHANDLE) { owner = getWindowFromHWND(anchor, hwndParent); if (!owner) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, NULLHANDLE); } } // make space for the new window struct in the anchor block Window *win = NULL; FIXME("optimize this"); for (size_t i = 0; i < anchor->windows_array_len; i++) { if (anchor->windows[i].hwnd == NULLHANDLE) { win = &anchor->windows[i]; break; } } if (!win) { const size_t len = anchor->windows_array_len; void *ptr = realloc(anchor->windows, sizeof (Window) * len * 2); if (!ptr) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_HEAP_OUT_OF_MEMORY, NULLHANDLE); } anchor->windows = (Window *) ptr; memset(&anchor->windows[len], '\0', sizeof (Window) * len); win = &anchor->windows[len]; anchor->windows_array_len *= 2; } char *text = NULL; if (pszName) { // !!! FIXME: text isn't necessarily a string. It's window-class-specific data. text = strdup(pszName); if (!text) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_HEAP_OUT_OF_MEMORY, NULLHANDLE); } } void *data = NULL; if (winclass->data_len > 0) { data = malloc(winclass->data_len); if (!data) { free(text); SET_WIN_ERROR_AND_RETURN(anchor, PMERR_HEAP_OUT_OF_MEMORY, NULLHANDLE); } } if (hwndParent == HWND_DESKTOP) { // toplevel? build a heavy weight window. Uint32 sdlflags = SDL_WINDOW_HIDDEN | SDL_WINDOW_BORDERLESS; // !!! FIXME SDL_WINDOW_SKIP_TASKBAR = 0x00010000, /**< window should not be added to the taskbar */ // !!! FIXME: text isn't necessarily a string. It's window-class-specific data. // The Y coordinate is the bottom of the window, counted from the bottom of the parent window (desktop, in this case) :/ SDL_Window *window = SDL_CreateWindow(text ? text : "", x, (desktop_window.h - y) - cy, cx, cy, sdlflags); if (!window) { free(text); free(data); FIXME("error code?"); SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INTERNAL_ERROR_1, NULLHANDLE); } SDL_SetWindowData(window, WINDATA_WINDOWPTR_NAME, win); SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_TARGETTEXTURE); if (!renderer) { free(text); free(data); SDL_DestroyWindow(window); FIXME("error code?"); SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INTERNAL_ERROR_1, NULLHANDLE); } SDL_RendererInfo info; SDL_GetRendererInfo(renderer, &info); FIXME("handle failure here"); SDL_Texture *texture = SDL_CreateTexture(renderer, info.texture_formats[0], SDL_TEXTUREACCESS_TARGET, cx, cy); if (!texture) { free(text); free(data); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); FIXME("error code?"); SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INTERNAL_ERROR_1, NULLHANDLE); } SDL_SetRenderTarget(renderer, texture); win->heavy.window = window; win->heavy.renderer = renderer; win->heavy.texture = texture; } else { // use same heavy as parent. win->heavy.window = parent->heavy.window; win->heavy.renderer = parent->heavy.renderer; win->heavy.texture = parent->heavy.texture; } win->parent = parent; win->children = NULL; win->hwnd = (HWND) (((size_t) (win - anchor->windows)) + FIRST_HWND_VALUE); win->window_class_name = winclass->name; // this pointer lives until Anchor Block terminates. win->data = data; win->text = text; win->data_len = winclass->data_len; win->window_proc = winclass->window_proc; win->class_style = winclass->style; win->style = flStyle; win->id = id; win->owner = owner; win->sibling = NULL; // we'll insert this later. win->x = win->y = win->w = win->h = 0; // we'll set these later. // we're ready, send WM_CREATE. Note that pCtlData has to be a pointer (not // an integer cast to a pointer), because OS/2 would dereference it, and the // 16 bits at the start of the data it points to tell you how many bytes are // pointed at, so it could deal with 16-bit code. We don't (currently) care, // so just pass it on. if (sendMessage(win, WM_CREATE, pCtlData, pPresParams) != FALSE) { // response to WM_CREATE was to cancel window creation!! destroyWindow(anchor, win); return NULLHANDLE; } FIXME("should I set swp.fl here?"); SWP swp = { 0, cy, cx, y, x, hwndInsertBehind, win->hwnd, 0, 0 }; MRESULT adjust = sendMessage(win, WM_ADJUSTWINDOWPOS, &swp, 0); (void) adjust; FIXME("adjust based on WM_ADJUSTWINDOWPOS message result"); win->x = swp.x; win->y = swp.y; win->w = swp.cx; win->h = swp.cy; hwndInsertBehind = swp.hwndInsertBehind; Window *insertBehind = NULL; if ((hwndInsertBehind != HWND_TOP) && (hwndInsertBehind != HWND_BOTTOM)) { insertBehind = getWindowFromHWND(anchor, hwndInsertBehind); if (!insertBehind || (insertBehind->parent != parent)) { FIXME("should we fail or just insert at top/bottom?"); hwndInsertBehind = HWND_BOTTOM; insertBehind = NULL; } } if (parent->children == NULL) { // nothing to insert behind, just make it the only child. parent->children = win; } else if (hwndInsertBehind == HWND_TOP) { win->sibling = parent->children; parent->children = win; } else if (hwndInsertBehind == HWND_BOTTOM) { Window *prev = NULL; for (Window *i = parent->children; i != NULL; i = i->sibling) { prev = i; } prev->sibling = win; } else { // insert behind a specific window. win->sibling = insertBehind->sibling; insertBehind->sibling = win; } if (hwndParent == HWND_DESKTOP) { SDL_SetWindowPosition(win->heavy.window, win->x, (parent->h - win->y) - win->h); SDL_SetWindowSize(win->heavy.window, win->w, win->h); if (win->style & WS_VISIBLE) { // !!! FIXME: mark window region invalid SDL_ShowWindow(win->heavy.window); } } return win->hwnd; } // WinCreateWindow HWND WinCreateStdWindow(HWND hwndParent, ULONG flStyle, PULONG pflCreateFlags, PSZ pszClientClass, PSZ pszTitle, ULONG styleClient, HMODULE hmod, ULONG idResources, PHWND phwndClient) { TRACE_NATIVE("WinCreateStdWindow(%u, %u, %p, '%s', '%s', %u, %u, %u, %p)", (uint) hwndParent, (uint) flStyle, pflCreateFlags, pszClientClass, pszTitle, (uint) styleClient, (uint) hmod, (uint) idResources, phwndClient); if (phwndClient) { *phwndClient = NULLHANDLE; } FRAMECDATA fcd = { sizeof (FRAMECDATA), *pflCreateFlags, hmod, idResources }; HWND retval = WinCreateWindow(hwndParent, (PSZ) WC_FRAME, pszTitle, 0, 0, 0, 0, 0, NULLHANDLE, HWND_TOP, idResources, &fcd, NULL); if (!retval) { return NULLHANDLE; } if (pszClientClass) { HWND clientwin = WinCreateWindow(retval, pszClientClass, NULL, 0, 0, 0, 0, 0, retval, HWND_BOTTOM, FID_CLIENT, NULL, NULL); if (!clientwin) { WinDestroyWindow(retval); return NULLHANDLE; } if (phwndClient) { *phwndClient = clientwin; } } return retval; } // WinCreateStdWindow BOOL WinDestroyWindow(HWND hwnd) { TRACE_NATIVE("WinDestroyWindow(%u)", (uint) hwnd); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { return FALSE; } Window *win = getWindowFromHWND(anchor, hwnd); if (!win) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, FALSE); } destroyWindow(anchor, win); return TRUE; } // WinDestroyWindow MRESULT WinDefWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) { TRACE_NATIVE("WinDefWindowProc(%u, %u, %p, %p)", (uint) hwnd, (uint) msg, mp1, mp2); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { return (MRESULT) 0; } Window *win = getWindowFromHWND(anchor, hwnd); if (!win) { return (MRESULT) 0; } switch (msg) { // this whole block just returns zero without further processing. case WM_ACTIVATE: case WM_APPTERMINATENOTIFY: case WM_ADJUSTWINDOWPOS: case WM_CALCFRAMERECT: case WM_COMMAND: case WM_CONTROL: case WM_CREATE: case WM_CTLCOLORCHANGE: case WM_DESTROY: case WM_DRAWITEM: case WM_ENABLE: case WM_ERASEBACKGROUND: case WM_ERROR: case WM_FORMATFRAME: case WM_HSCROLL: case WM_INITDLG: case WM_INITMENU: case WM_JOURNALNOTIFY: case WM_MATCHMNEMONIC: case WM_MEASUREITEM: case WM_MENUEND: case WM_MINMAXFRAME: case WM_MOUSEMAP: case WM_MOVE: case WM_NEXTMENU: case WM_NULL: case WM_PCONTROL: case WM_PRESPARAMCHANGED: case WM_PSETFOCUS: case WM_PSIZE: case WM_PSYSCOLORCHANGE: case WM_QUERYACCELTABLE: case WM_QUIT: case WM_SAVEAPPLICATION: case WM_SEM1: case WM_SEM2: case WM_SEM3: case WM_SEM4: case WM_SETACCELTABLE: case WM_SETFOCUS: case WM_SETHELPINFO: case WM_SETSELECTION: case WM_SETWINDOWPARAMS: case WM_SHOW: case WM_SIZE: case WM_SUBSTITUTESTRING: case WM_SYSCOLORCHANGE: case WM_SYSCOMMAND: case WM_SYSVALUECHANGED: case WM_TIMER: case WM_TRACKFRAME: case WM_TRANSLATEACCEL: //case WM_TRANSLATEMNEMONIC: // !!! FIXME: this is in the SDK reference but not the SDK headers. case WM_UPDATEFRAME: case WM_VRNDISABLED: case WM_VRNENABLED: case WM_VSCROLL: // the SDK reference manual doesn't list default actions for these messages, but it's probably just "return zero". case WM_MSGBOXINIT: case WM_MSGBOXDISMISS: return (MRESULT) 0; // this whole block just returns one without further processing. case WM_MENUSELECT: return (MRESULT) 1; // this block sends the message to the window owner (if one exists), or otherwise returns 0. case WM_BEGINDRAG: case WM_BEGINSELECT: case WM_BUTTON1CLICK: case WM_BUTTON1DBLCLK: case WM_BUTTON1MOTIONSTART: case WM_BUTTON1MOTIONEND: case WM_BUTTON1UP: case WM_BUTTON2CLICK: case WM_BUTTON2DBLCLK: case WM_BUTTON2MOTIONSTART: case WM_BUTTON2MOTIONEND: case WM_BUTTON2UP: case WM_BUTTON3CLICK: case WM_BUTTON3DBLCLK: case WM_BUTTON3MOTIONSTART: case WM_BUTTON3MOTIONEND: case WM_BUTTON3UP: case WM_CHAR: case WM_CHORD: case WM_CONTEXTMENU: case WM_ENDDRAG: case WM_ENDSELECT: case WM_OPEN: case WM_PACTIVATE: case WM_QUERYCTLTYPE: case WM_QUERYHELPINFO: case WM_QUERYTRACKINFO: case WM_SINGLESELECT: case WM_TEXTEDIT: return win->owner ? sendMessage(win->owner, msg, mp1, mp2) : (MRESULT) 0; // These messages actually do something specific in default processing... case WM_HITTEST: return HT_NORMAL; case WM_BUTTON1DOWN: case WM_BUTTON2DOWN: case WM_BUTTON3DOWN: case WM_CALCVALIDRECTS: case WM_CLOSE: case WM_CONTROLPOINTER: case WM_FOCUSCHANGE: case WM_HELP: case WM_MOUSEMOVE: case WM_PAINT: case WM_PPAINT: case WM_QUERYCONVERTPOS: case WM_QUERYWINDOWPARAMS: case WM_REALIZEPALETTE: case WM_WINDOWPOSCHANGED: fprintf(stderr, "Message that doesn't something other than return 0, %u, in WinDefWindowProc!\n", (uint) msg); break; default: fprintf(stderr, "Unhandled message %u in WinDefWindowProc!\n", (uint) msg); break; } return (MRESULT) 0; } // WinDefWindowProc HPS WinBeginPaint(HWND hwnd, HPS hps, PRECTL prclPaint) { TRACE_NATIVE("WinBeginPaint(%u, %u, %p)", (uint) hwnd, (uint) hps, prclPaint); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { return NULLHANDLE; } Window *win = getWindowFromHWND(anchor, hwnd); if (!win) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, NULLHANDLE); } PresentationSpace *ps = NULL; if (hps != NULLHANDLE) { ps = getPresentationSpaceFromHPS(anchor, hps); if (!ps) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INV_HPS, NULLHANDLE); } FIXME("check if this is already assigned to this drawable"); } else { // make space for the new presentation struct in the anchor block FIXME("optimize this"); FIXME("cut-and-paste from WinCreateWindow"); for (size_t i = 0; i < anchor->pres_spaces_array_len; i++) { if (anchor->pres_spaces[i].hps == NULLHANDLE) { ps = &anchor->pres_spaces[i]; break; } } if (!ps) { const size_t len = anchor->pres_spaces_array_len; void *ptr = realloc(anchor->pres_spaces, sizeof (PresentationSpace) * len * 2); if (!ptr) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_HEAP_OUT_OF_MEMORY, NULLHANDLE); } anchor->pres_spaces = (PresentationSpace *) ptr; memset(&anchor->pres_spaces[len], '\0', sizeof (PresentationSpace) * len); ps = &anchor->pres_spaces[len]; anchor->pres_spaces_array_len *= 2; } hps = (HPS) (((size_t) (ps - anchor->pres_spaces)) + FIRST_HPS_VALUE); } ps->hps = hps; ps->window = win; if (prclPaint) { FIXME("report the update region, not the whole window"); prclPaint->xLeft = 0; prclPaint->yBottom = 0; prclPaint->xRight = win->w; prclPaint->yTop = win->h; } return hps; } // WinBeginPaint BOOL WinEndPaint(HPS hps) { TRACE_NATIVE("WinEndPaint(%u)", (uint) hps); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { return NULLHANDLE; } PresentationSpace *ps = getPresentationSpaceFromHPS(anchor, hps); if (!ps) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INV_HPS, NULLHANDLE); } Window *win = ps->window; SDL_Renderer *renderer = win->heavy.renderer; SDL_Texture *texture = win->heavy.texture; assert(SDL_GetRenderTarget(renderer) == texture); SDL_SetRenderTarget(renderer, NULL); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); SDL_SetRenderTarget(renderer, texture); destroyPresentationSpace(anchor, ps); return TRUE; } // WinEndPaint static void os2ColorIndexToRGB(const LONG os2color, Uint8 *r, Uint8 *g, Uint8 *b) { assert(r != NULL); assert(g != NULL); assert(b != NULL); #define RETURNRGB(rr,gg,bb) { *r = rr; *g = gg; *b = bb; return; } switch (os2color) { case CLR_FALSE: FIXME("this should reverse on a printer"); RETURNRGB(0x00, 0x00, 0x00); case CLR_TRUE: FIXME("this should reverse on a printer"); RETURNRGB(0xFF, 0xFF, 0xFF); case CLR_DEFAULT: FIXME("Let users specify this on command line or system-wide .ini?"); RETURNRGB(0x00, 0x00, 0x00); case CLR_WHITE: RETURNRGB(0xFF, 0xFF, 0xFF); case CLR_BLACK: RETURNRGB(0x00, 0x00, 0x00); case CLR_BACKGROUND: FIXME("Let users specify this on command line or system-wide .ini?"); RETURNRGB(0xFF, 0xFF, 0xFF); case CLR_BLUE: RETURNRGB(0x00, 0x00, 0xFF); case CLR_RED: RETURNRGB(0xFF, 0x00, 0x00); case CLR_PINK: RETURNRGB(0xFF, 0x00, 0xFF); case CLR_GREEN: RETURNRGB(0x00, 0xFF, 0x00); case CLR_CYAN: RETURNRGB(0x00, 0xFF, 0xFF); case CLR_YELLOW: RETURNRGB(0xFF, 0xFF, 0x00); case CLR_NEUTRAL: FIXME("Let users specify this on command line or system-wide .ini?"); RETURNRGB(0x00, 0x00, 0x00); case CLR_DARKGRAY: RETURNRGB(0x80, 0x80, 0x80); case CLR_DARKBLUE: RETURNRGB(0x00, 0x00, 0x80); case CLR_DARKRED: RETURNRGB(0x80, 0x00, 0x00); case CLR_DARKPINK: RETURNRGB(0x80, 0x00, 0x80); case CLR_DARKGREEN: RETURNRGB(0x00, 0x80, 0x00); case CLR_DARKCYAN: RETURNRGB(0x00, 0x80, 0x80); case CLR_BROWN: RETURNRGB(0x80, 0x80, 0x00); case CLR_PALEGRAY: RETURNRGB(0xCC, 0xCC, 0xCC); default: break; } FIXME("unknown color index"); RETURNRGB(0x00, 0x00, 0x00); #undef RETURNRGB } // os2ColorIndexToRGB static void calcHeavyweightRect(const Window *win, SDL_Rect* rect) { int x = 0, y = 0; rect->w = win->w; rect->h = win->h; while (win->parent != &desktop_window) { x += win->x; y += win->y; win = win->parent; } rect->x = x; rect->y = (win->h - y) - rect->h; } // calcHeavyweightRect BOOL WinFillRect(HPS hps, PRECTL prcl, LONG lColor) { TRACE_NATIVE("WinFillRect(%u, %p, %d)", (uint) hps, prcl, (int) lColor); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { return NULLHANDLE; } PresentationSpace *ps = getPresentationSpaceFromHPS(anchor, hps); if (!ps) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INV_HPS, NULLHANDLE); } Window *win = ps->window; SDL_Renderer *renderer = win->heavy.renderer; assert(SDL_GetRenderTarget(renderer) == win->heavy.texture); SDL_Rect cliprect; calcHeavyweightRect(win, &cliprect); SDL_Rect rect; rect.x = cliprect.x + prcl->xLeft; rect.y = cliprect.y + (cliprect.h - prcl->yTop); rect.w = prcl->xRight - prcl->xLeft; rect.h = prcl->yTop - prcl->yBottom; Uint8 r, g, b; os2ColorIndexToRGB(lColor, &r, &g, &b); //printf("fill { x=%d, y=%d, w=%d, h=%d, r=0x%X, g=0x%X, b=0x%X }\n", (int) rect.x, (int) rect.y, (int) rect.w, (int) rect.h, (uint) r, (uint) g, (uint) b); SDL_RenderSetClipRect(renderer, &cliprect); SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF); SDL_RenderFillRect(renderer, &rect); SDL_RenderSetClipRect(renderer, NULL); return TRUE; } // WinFillRect BOOL WinIsWindow(HAB hab, HWND hwnd) { TRACE_NATIVE("WinIsWindow(%u, %u)", (uint) hab, (uint) hwnd); FIXME("this 'get the anchor, get the window, set errors' code is repeated a lot"); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, FALSE); } Window *win = getWindowFromHWND(anchor, hwnd); if (!win) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, FALSE); } return TRUE; } // WinIsWindow BOOL WinIsWindowEnabled(HWND hwnd) { TRACE_NATIVE("WinIsWindowEnabled(%u)", (uint) hwnd); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, FALSE); } Window *win = getWindowFromHWND(anchor, hwnd); if (!win) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, FALSE); } return win->enabled; } // WinIsWindowEnabled BOOL WinIsWindowVisible(HWND hwnd) { TRACE_NATIVE("WinIsWindowVisible(%u)", (uint) hwnd); AnchorBlock *anchor = getAnchorBlockNoHAB(); if (!anchor) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, FALSE); } Window *win = getWindowFromHWND(anchor, hwnd); if (!win) { SET_WIN_ERROR_AND_RETURN(anchor, PMERR_INVALID_HWND, FALSE); } return ((win->style & WS_VISIBLE) == WS_VISIBLE) ? TRUE : FALSE; } // WinIsWindowVisible // end of pmwin.c ...