Mercurial > hg > xemacs-beta
diff src/msw-proc.c @ 213:78f53ef88e17 r20-4b5
Import from CVS: tag r20-4b5
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:06:47 +0200 |
parents | |
children | d44af0c54775 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/msw-proc.c Mon Aug 13 10:06:47 2007 +0200 @@ -0,0 +1,602 @@ +/* mswindows specific event-handling. + Copyright (C) 1997 Jonathan Harris. + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs 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 General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +/* Authorship: + + Jonathan Harris, November 1997 for 20.4. + */ + +/* + * Comment: + * + * Windows user-input type events are stored in a per-thread message queue + * and retrieved using GetMessage(). It is not possible to wait on this + * queue and on other events (eg process input) simultaneously. Also, the + * main event-handling code in windows (the "windows procedure") is called + * asynchronously when windows has certain other types of events ("nonqueued + * messages") to deliver. The documentation doesn't appear to specify the + * context in which the windows procedure is called, but I assume that the + * thread that created the window is temporarily highjacked for this purpose. + * + * We spawn off a single thread to deal with both kinds of messages. The + * thread turns the windows events into emacs_events and stuffs them in a + * queue which XEmacs reads at its leisure. This file contains the code for + * the thread. This scheme also helps to prevent weird synchronisation and + * deadlock problems that might occur if the windows procedure was called + * when XEmacs was already in the middle of processing an event. + * + * Unfortunately, only the thread that created a window can retrieve messages + * destined for that window ("GetMessage does not retrieve messages for + * windows that belong to other threads..."). This means that our message- + * processing thread also has to do all window creation. We handle this + * bogosity by getting the main XEmacs thread to send special user-defined + * messages to the message-processing thread to instruct it to create windows. + */ + + +#include <config.h> +#include "lisp.h" + +#include "console-msw.h" +#include "device.h" +#include "frame.h" +#include "events.h" +#include "event-msw.h" + +#define MSWINDOWS_FRAME_STYLE WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_TILEDWINDOW +#define MSWINDOWS_POPUP_STYLE WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_CAPTION|WS_POPUP + +static LRESULT WINAPI mswindows_wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); +static Lisp_Object mswindows_find_console (HWND hwnd); +static Lisp_Object mswindows_find_frame (HWND hwnd); +static Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key); + +/* + * Entry point for the "windows" message-processing thread + */ +DWORD mswindows_win_thread() +{ + WNDCLASS wc; + MSG msg; + mswindows_waitable_info_type info; + + /* Register the main window class */ + wc.style = CS_OWNDC; /* One DC per window */ + wc.lpfnWndProc = (WNDPROC) mswindows_wnd_proc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; /* ? */ + wc.hInstance = NULL; /* ? */ + wc.hIcon = LoadIcon (NULL, XEMACS_CLASS); + wc.hCursor = LoadCursor (NULL, IDC_ARROW); + wc.hbrBackground = NULL; /* GetStockObject (WHITE_BRUSH); */ + wc.lpszMenuName = NULL; /* XXX FIXME? Add a menu? */ + wc.lpszClassName = XEMACS_CLASS; + RegisterClass(&wc); /* XXX FIXME: Should use RegisterClassEx */ + + info.type = mswindows_waitable_type_dispatch; + mswindows_add_waitable(&info); + + /* Ensure our message queue is created XXX FIXME: Is this necessary? */ + PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE); + + /* Notify the main thread that we're ready */ + assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, 0)); + + /* Main windows loop */ + while (1) + { + GetMessage (&msg, NULL, 0, 0); + + /* + * Process things that don't have an associated window, so wouldn't + * get sent to mswindows_wnd_proc + */ + + /* Request from main thread */ + if (msg.message>=WM_XEMACS_BASE && msg.message<=WM_XEMACS_END) + mswindows_handle_request(&msg); + + /* Timeout */ + else if (msg.message == WM_TIMER) + { + Lisp_Object emacs_event; + struct Lisp_Event *event; + + KillTimer(NULL, msg.wParam); + EnterCriticalSection (&mswindows_dispatch_crit); + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + event->channel = Qnil; + event->timestamp = msg.time; + event->event_type = timeout_event; + event->event.timeout.interval_id = msg.wParam; + mswindows_enqueue_dispatch_event (emacs_event); + LeaveCriticalSection (&mswindows_dispatch_crit); + } + else + /* Pass on to mswindows_wnd_proc */ + DispatchMessage (&msg); + } +} + +/* + * The windows procedure for the window class XEMACS_CLASS + * Stuffs messages in the mswindows event queue + */ +static LRESULT WINAPI mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam) +{ + /* Note: Remember to initialise these before use */ + Lisp_Object emacs_event; + struct Lisp_Event *event; + + static int mods = 0; + MSG msg = { hwnd, message, wParam, lParam, 0, {0,0} }; + msg.time = GetMessageTime(); + +#if 0 /* XXX */ + stderr_out("Message %04x, wParam=%04x, lParam=%08lx\n", message, wParam, lParam); +#endif + switch (message) + { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + switch(wParam) + { + case VK_SHIFT: + mods |= MOD_SHIFT; + break; + case VK_CONTROL: + mods |= MOD_CONTROL; + break; + case VK_MENU: + mods |= MOD_META; + break; + default: + /* Handle those keys that TranslateMessage won't generate a WM_CHAR for */ + { + Lisp_Object keysym; + if (!NILP (keysym = mswindows_key_to_emacs_keysym(wParam))) + { + EnterCriticalSection (&mswindows_dispatch_crit); + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + event->channel = mswindows_find_console(hwnd); + event->timestamp = msg.time; + event->event_type = key_press_event; + event->event.key.keysym = keysym; + event->event.key.modifiers = mods; + mswindows_enqueue_dispatch_event (emacs_event); + LeaveCriticalSection (&mswindows_dispatch_crit); + return (0); + } + } + } + TranslateMessage (&msg); /* Maybe generates WM_[SYS]CHAR in message queue */ + goto defproc; + + case WM_KEYUP: + case WM_SYSKEYUP: + switch(wParam) + { + case VK_SHIFT: + mods &= ~MOD_SHIFT; + break; + case VK_CONTROL: + mods &= ~MOD_CONTROL; + break; + case VK_MENU: + mods &= ~MOD_META; + break; + } + TranslateMessage (&msg); + goto defproc; + + case WM_CHAR: + case WM_SYSCHAR: + { + EnterCriticalSection (&mswindows_dispatch_crit); + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + event->channel = mswindows_find_console(hwnd); + event->timestamp = msg.time; + event->event_type = key_press_event; + event->event.key.modifiers = mods; + event->event.key.modifiers = lParam & 0x20000000 ? MOD_META : 0; /* redundant? */ + if (wParam<' ') /* Control char not handled under WM_KEYDOWN */ + { + event->event.key.keysym = make_char(wParam+'a'-1); + event->event.key.modifiers |= MOD_CONTROL; /* redundant? */ + } + else + { + /* Assumes that emacs keysym == ASCII code */ + event->event.key.keysym = make_char(wParam); + } + mswindows_enqueue_dispatch_event (emacs_event); + LeaveCriticalSection (&mswindows_dispatch_crit); + } + break; + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + { + /* XXX FIXME: Do middle button emulation */ + EnterCriticalSection (&mswindows_dispatch_crit); + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + event->channel = mswindows_find_frame(hwnd); + event->timestamp = msg.time; + event->event_type = + (message==WM_LBUTTONDOWN || message==WM_MBUTTONDOWN || + message==WM_RBUTTONDOWN) ? + button_press_event : button_release_event; +#if 0 + ((wParam & MK_CONTROL) ? MOD_CONTROL : 0) | + ((wParam & MK_SHIFT) ? MOD_SHIFT : 0); +#endif + event->event.button.button = + (message==WM_LBUTTONDOWN || message==WM_LBUTTONUP) ? 1 : + ((message==WM_RBUTTONDOWN || message==WM_RBUTTONUP) ? 3 : 2); + event->event.button.x = LOWORD(lParam); + event->event.button.y = HIWORD(lParam); + event->event.button.modifiers = mods; + + mswindows_enqueue_dispatch_event (emacs_event); + LeaveCriticalSection (&mswindows_dispatch_crit); + } + break; + + case WM_MOUSEMOVE: + { + EnterCriticalSection (&mswindows_dispatch_crit); + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + event->channel = mswindows_find_frame(hwnd); + event->timestamp = msg.time; + event->event_type = pointer_motion_event; + event->event.motion.x = LOWORD(lParam); + event->event.motion.y = HIWORD(lParam); + event->event.motion.modifiers = mods; + + mswindows_enqueue_dispatch_event (emacs_event); + LeaveCriticalSection (&mswindows_dispatch_crit); + } + break; + + case WM_PAINT: + if (GetUpdateRect(hwnd, NULL, FALSE)) + { + PAINTSTRUCT paintStruct; + + EnterCriticalSection (&mswindows_dispatch_crit); + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + event->channel = mswindows_find_frame(hwnd); + event->timestamp = msg.time; + event->event_type = magic_event; + BeginPaint (hwnd, &paintStruct); + EVENT_MSWINDOWS_MAGIC_TYPE(event) = message; + EVENT_MSWINDOWS_MAGIC_DATA(event) = paintStruct.rcPaint; + EndPaint (hwnd, &paintStruct); + + mswindows_enqueue_dispatch_event (emacs_event); + LeaveCriticalSection (&mswindows_dispatch_crit); + } + break; + + case WM_SIZE: + /* We only care about this message if our size has really changed */ + if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED) + { + RECT rect; + EnterCriticalSection (&mswindows_dispatch_crit); + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + event->channel = mswindows_find_frame(hwnd); + event->timestamp = msg.time; + event->event_type = magic_event; + if (wParam==SIZE_MINIMIZED) + rect.left = rect.top = rect.right = rect.bottom = -1; + else + GetClientRect(hwnd, &rect); + EVENT_MSWINDOWS_MAGIC_TYPE(event) = message; + EVENT_MSWINDOWS_MAGIC_DATA(event) = rect; + + mswindows_enqueue_dispatch_event (emacs_event); + LeaveCriticalSection (&mswindows_dispatch_crit); + } + break; + + case WM_SETFOCUS: + case WM_KILLFOCUS: + { + EnterCriticalSection (&mswindows_dispatch_crit); + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + event->channel = mswindows_find_frame(hwnd); + event->timestamp = msg.time; + event->event_type = magic_event; + EVENT_MSWINDOWS_MAGIC_TYPE(event) = message; + + mswindows_enqueue_dispatch_event (emacs_event); + LeaveCriticalSection (&mswindows_dispatch_crit); + } + break; + + case WM_QUIT: + /* XXX FIXME: Should do something here! */ + defproc: + default: + return DefWindowProc (hwnd, message, wParam, lParam); + } + return (0); +} + + +/* + * Make a request to the message-processing thread to do things that + * can't be done in the main thread. + */ +LPARAM +mswindows_make_request(UINT message, WPARAM wParam, mswindows_request_type *request) +{ + MSG msg; + assert(PostThreadMessage (mswindows_win_thread_id, message, wParam, + (LPARAM) request)); + GetMessage (&msg, NULL, WM_XEMACS_ACK, WM_XEMACS_ACK); + return (msg.lParam); +} + + +/* + * Handle a request from the main thread to do things that have to be + * done in the message-processing thread. + */ +static void +mswindows_handle_request (MSG *msg) +{ + mswindows_request_type *request = (mswindows_request_type *) msg->lParam; + + switch (msg->message) + { + case WM_XEMACS_CREATEWINDOW: + { + struct frame *f = request->thing1; + Lisp_Object *props = request->thing2; + Lisp_Object name, height, width, popup, top, left; + RECT rect; + DWORD style; + HWND hwnd; + + name = Fplist_get (*props, Qname, Qnil); + height = Fplist_get (*props, Qheight, Qnil); + width = Fplist_get (*props, Qwidth, Qnil); + popup = Fplist_get (*props, Qpopup, Qnil); + top = Fplist_get (*props, Qtop, Qnil); + left = Fplist_get (*props, Qleft, Qnil); + + style = (NILP(popup)) ? MSWINDOWS_FRAME_STYLE : MSWINDOWS_POPUP_STYLE; + + rect.left = rect.top = 0; + rect.right = INTP(width) ? XINT(width) : 640; + rect.bottom = INTP(height) ? XINT(height) : 480; +#ifdef HAVE_MENUBARS + AdjustWindowRect(&rect, style, TRUE); +#else + AdjustWindowRect(&rect, style, FALSE); +#endif + + hwnd = CreateWindow (XEMACS_CLASS, + STRINGP(f->name) ? XSTRING_DATA(f->name) : + (STRINGP(name) ? XSTRING_DATA(name) : XEMACS_CLASS), + style, + INTP(left) ? XINT(left) : CW_USEDEFAULT, + INTP(top) ? XINT(top) : CW_USEDEFAULT, + rect.right-rect.left, rect.bottom-rect.top, + NULL, NULL, NULL, NULL); + assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, (LPARAM) hwnd)); + } + return; + + case WM_XEMACS_SETTIMER: + { + UINT id; + id=SetTimer (NULL, 0, (UINT) request->thing1, NULL); + assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, id)); + } + break; + + case WM_XEMACS_KILLTIMER: + { + KillTimer (NULL, (UINT) request->thing1); + assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, 0)); + } + break; + + default: + assert(0); + } +} + + +/* + * Translate a mswindows virtual key to a keysym. + * Only returns non-Qnil for keys that don't generate WM_CHAR messages + * or whose ASCII codes (like space) xemacs doesn't like. + * Virtual key values are defined in winresrc.h + * XXX I'm not sure that KEYSYM("name") is the best thing to use here. + */ +Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key) +{ + switch (mswindows_key) + { + /* First the predefined ones */ + case VK_BACK: return QKbackspace; + case VK_TAB: return QKtab; + case '\n': return QKlinefeed; /* No VK_LINEFEED in winresrc.h */ + case VK_RETURN: return QKreturn; + case VK_ESCAPE: return QKescape; + case VK_SPACE: return QKspace; + case VK_DELETE: return QKdelete; + + /* The rest */ + case VK_PRIOR: return KEYSYM ("prior"); + case VK_NEXT: return KEYSYM ("next"); + case VK_END: return KEYSYM ("end"); + case VK_HOME: return KEYSYM ("home"); + case VK_LEFT: return KEYSYM ("left"); + case VK_UP: return KEYSYM ("up"); + case VK_RIGHT: return KEYSYM ("right"); + case VK_DOWN: return KEYSYM ("down"); + case VK_INSERT: return KEYSYM ("insert"); + case VK_HELP: return KEYSYM ("help"); + case VK_F1: return KEYSYM ("F1"); + case VK_F2: return KEYSYM ("F2"); + case VK_F3: return KEYSYM ("F3"); + case VK_F4: return KEYSYM ("F4"); + case VK_F5: return KEYSYM ("F5"); + case VK_F6: return KEYSYM ("F6"); + case VK_F7: return KEYSYM ("F7"); + case VK_F8: return KEYSYM ("F8"); + case VK_F9: return KEYSYM ("F9"); + case VK_F10: return KEYSYM ("F10"); + case VK_F11: return KEYSYM ("F11"); + case VK_F12: return KEYSYM ("F12"); + case VK_F13: return KEYSYM ("F13"); + case VK_F14: return KEYSYM ("F14"); + case VK_F15: return KEYSYM ("F15"); + case VK_F16: return KEYSYM ("F16"); + case VK_F17: return KEYSYM ("F17"); + case VK_F18: return KEYSYM ("F18"); + case VK_F19: return KEYSYM ("F19"); + case VK_F20: return KEYSYM ("F20"); + case VK_F21: return KEYSYM ("F21"); + case VK_F22: return KEYSYM ("F22"); + case VK_F23: return KEYSYM ("F23"); + case VK_F24: return KEYSYM ("F24"); + } + return Qnil; +} + + +/* + * Find the console that matches the supplied mswindows window handle + */ +static Lisp_Object +mswindows_find_console (HWND hwnd) +{ + Lisp_Object concons; + + CONSOLE_LOOP (concons) + { + Lisp_Object console = XCAR (concons); + /* We only support one console so this must be it */ + return console; + } + + return Qnil; +} + +/* + * Find the frame that matches the supplied mswindows window handle + */ +static Lisp_Object +mswindows_find_frame (HWND hwnd) +{ + Lisp_Object frmcons, devcons, concons; + + FRAME_LOOP_NO_BREAK (frmcons, devcons, concons) + { + struct frame *f; + Lisp_Object frame = XCAR (frmcons); + f = XFRAME (frame); + if (FRAME_TYPE_P(f, mswindows)) /* Might be a stream-type frame */ + if (FRAME_MSWINDOWS_HANDLE(f)==hwnd) + return frame; + } + assert(0); /* XXX Can't happen! we only get messages for our windows */ + return Qnil; +} + +/* + * Random helper functions for debugging. + * Intended for use in the MSVC "Watch" window which doesn't like + * the aborts that the error_check_foo() functions can make. + */ +struct lrecord_header *DHEADER(Lisp_Object obj) +{ + return LRECORDP(obj) ? XRECORD_LHEADER(obj) : NULL; + /* (lrecord_header*)(obj & 0xfffffff) */ +} + +struct Lisp_Event *DEVENT(Lisp_Object obj) +{ + return (EVENTP (obj)) ? XEVENT(obj) : NULL; +} + +struct Lisp_Cons *DCONS(Lisp_Object obj) +{ + return (CONSP (obj)) ? XCONS(obj) : NULL; +} + +Lisp_Object DCAR(Lisp_Object obj) +{ + return (CONSP (obj)) ? XCAR(obj) : 0; +} + +Lisp_Object DCDR(Lisp_Object obj) +{ + return (CONSP (obj)) ? XCDR(obj) : 0; +} + +char *DSTRING(Lisp_Object obj) +{ + return (STRINGP (obj)) ? XSTRING_DATA(obj) : NULL; +} + +struct Lisp_Vector *DVECTOR(Lisp_Object obj) +{ + return (VECTORP (obj)) ? XVECTOR(obj) : NULL; +} + +struct Lisp_Symbol *DSYMBOL(Lisp_Object obj) +{ + return (SYMBOLP (obj)) ? XSYMBOL(obj) : NULL; +} + +char *DSYMNAME(Lisp_Object obj) +{ + return (SYMBOLP (obj)) ? XSYMBOL(obj)->name->_data : NULL; +}