Mercurial > hg > xemacs-beta
comparison 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 |
comparison
equal
deleted
inserted
replaced
212:d8688acf4c5b | 213:78f53ef88e17 |
---|---|
1 /* mswindows specific event-handling. | |
2 Copyright (C) 1997 Jonathan Harris. | |
3 | |
4 This file is part of XEmacs. | |
5 | |
6 XEmacs is free software; you can redistribute it and/or modify it | |
7 under the terms of the GNU General Public License as published by the | |
8 Free Software Foundation; either version 2, or (at your option) any | |
9 later version. | |
10 | |
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 for more details. | |
15 | |
16 You should have received a copy of the GNU General Public License | |
17 along with XEmacs; see the file COPYING. If not, write to | |
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 Boston, MA 02111-1307, USA. */ | |
20 | |
21 /* Synched up with: Not in FSF. */ | |
22 | |
23 /* Authorship: | |
24 | |
25 Jonathan Harris, November 1997 for 20.4. | |
26 */ | |
27 | |
28 /* | |
29 * Comment: | |
30 * | |
31 * Windows user-input type events are stored in a per-thread message queue | |
32 * and retrieved using GetMessage(). It is not possible to wait on this | |
33 * queue and on other events (eg process input) simultaneously. Also, the | |
34 * main event-handling code in windows (the "windows procedure") is called | |
35 * asynchronously when windows has certain other types of events ("nonqueued | |
36 * messages") to deliver. The documentation doesn't appear to specify the | |
37 * context in which the windows procedure is called, but I assume that the | |
38 * thread that created the window is temporarily highjacked for this purpose. | |
39 * | |
40 * We spawn off a single thread to deal with both kinds of messages. The | |
41 * thread turns the windows events into emacs_events and stuffs them in a | |
42 * queue which XEmacs reads at its leisure. This file contains the code for | |
43 * the thread. This scheme also helps to prevent weird synchronisation and | |
44 * deadlock problems that might occur if the windows procedure was called | |
45 * when XEmacs was already in the middle of processing an event. | |
46 * | |
47 * Unfortunately, only the thread that created a window can retrieve messages | |
48 * destined for that window ("GetMessage does not retrieve messages for | |
49 * windows that belong to other threads..."). This means that our message- | |
50 * processing thread also has to do all window creation. We handle this | |
51 * bogosity by getting the main XEmacs thread to send special user-defined | |
52 * messages to the message-processing thread to instruct it to create windows. | |
53 */ | |
54 | |
55 | |
56 #include <config.h> | |
57 #include "lisp.h" | |
58 | |
59 #include "console-msw.h" | |
60 #include "device.h" | |
61 #include "frame.h" | |
62 #include "events.h" | |
63 #include "event-msw.h" | |
64 | |
65 #define MSWINDOWS_FRAME_STYLE WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_TILEDWINDOW | |
66 #define MSWINDOWS_POPUP_STYLE WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_CAPTION|WS_POPUP | |
67 | |
68 static LRESULT WINAPI mswindows_wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); | |
69 static Lisp_Object mswindows_find_console (HWND hwnd); | |
70 static Lisp_Object mswindows_find_frame (HWND hwnd); | |
71 static Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key); | |
72 | |
73 /* | |
74 * Entry point for the "windows" message-processing thread | |
75 */ | |
76 DWORD mswindows_win_thread() | |
77 { | |
78 WNDCLASS wc; | |
79 MSG msg; | |
80 mswindows_waitable_info_type info; | |
81 | |
82 /* Register the main window class */ | |
83 wc.style = CS_OWNDC; /* One DC per window */ | |
84 wc.lpfnWndProc = (WNDPROC) mswindows_wnd_proc; | |
85 wc.cbClsExtra = 0; | |
86 wc.cbWndExtra = 0; /* ? */ | |
87 wc.hInstance = NULL; /* ? */ | |
88 wc.hIcon = LoadIcon (NULL, XEMACS_CLASS); | |
89 wc.hCursor = LoadCursor (NULL, IDC_ARROW); | |
90 wc.hbrBackground = NULL; /* GetStockObject (WHITE_BRUSH); */ | |
91 wc.lpszMenuName = NULL; /* XXX FIXME? Add a menu? */ | |
92 wc.lpszClassName = XEMACS_CLASS; | |
93 RegisterClass(&wc); /* XXX FIXME: Should use RegisterClassEx */ | |
94 | |
95 info.type = mswindows_waitable_type_dispatch; | |
96 mswindows_add_waitable(&info); | |
97 | |
98 /* Ensure our message queue is created XXX FIXME: Is this necessary? */ | |
99 PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE); | |
100 | |
101 /* Notify the main thread that we're ready */ | |
102 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, 0)); | |
103 | |
104 /* Main windows loop */ | |
105 while (1) | |
106 { | |
107 GetMessage (&msg, NULL, 0, 0); | |
108 | |
109 /* | |
110 * Process things that don't have an associated window, so wouldn't | |
111 * get sent to mswindows_wnd_proc | |
112 */ | |
113 | |
114 /* Request from main thread */ | |
115 if (msg.message>=WM_XEMACS_BASE && msg.message<=WM_XEMACS_END) | |
116 mswindows_handle_request(&msg); | |
117 | |
118 /* Timeout */ | |
119 else if (msg.message == WM_TIMER) | |
120 { | |
121 Lisp_Object emacs_event; | |
122 struct Lisp_Event *event; | |
123 | |
124 KillTimer(NULL, msg.wParam); | |
125 EnterCriticalSection (&mswindows_dispatch_crit); | |
126 emacs_event = Fmake_event (Qnil, Qnil); | |
127 event = XEVENT(emacs_event); | |
128 | |
129 event->channel = Qnil; | |
130 event->timestamp = msg.time; | |
131 event->event_type = timeout_event; | |
132 event->event.timeout.interval_id = msg.wParam; | |
133 mswindows_enqueue_dispatch_event (emacs_event); | |
134 LeaveCriticalSection (&mswindows_dispatch_crit); | |
135 } | |
136 else | |
137 /* Pass on to mswindows_wnd_proc */ | |
138 DispatchMessage (&msg); | |
139 } | |
140 } | |
141 | |
142 /* | |
143 * The windows procedure for the window class XEMACS_CLASS | |
144 * Stuffs messages in the mswindows event queue | |
145 */ | |
146 static LRESULT WINAPI mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, | |
147 LPARAM lParam) | |
148 { | |
149 /* Note: Remember to initialise these before use */ | |
150 Lisp_Object emacs_event; | |
151 struct Lisp_Event *event; | |
152 | |
153 static int mods = 0; | |
154 MSG msg = { hwnd, message, wParam, lParam, 0, {0,0} }; | |
155 msg.time = GetMessageTime(); | |
156 | |
157 #if 0 /* XXX */ | |
158 stderr_out("Message %04x, wParam=%04x, lParam=%08lx\n", message, wParam, lParam); | |
159 #endif | |
160 switch (message) | |
161 { | |
162 case WM_KEYDOWN: | |
163 case WM_SYSKEYDOWN: | |
164 switch(wParam) | |
165 { | |
166 case VK_SHIFT: | |
167 mods |= MOD_SHIFT; | |
168 break; | |
169 case VK_CONTROL: | |
170 mods |= MOD_CONTROL; | |
171 break; | |
172 case VK_MENU: | |
173 mods |= MOD_META; | |
174 break; | |
175 default: | |
176 /* Handle those keys that TranslateMessage won't generate a WM_CHAR for */ | |
177 { | |
178 Lisp_Object keysym; | |
179 if (!NILP (keysym = mswindows_key_to_emacs_keysym(wParam))) | |
180 { | |
181 EnterCriticalSection (&mswindows_dispatch_crit); | |
182 emacs_event = Fmake_event (Qnil, Qnil); | |
183 event = XEVENT(emacs_event); | |
184 | |
185 event->channel = mswindows_find_console(hwnd); | |
186 event->timestamp = msg.time; | |
187 event->event_type = key_press_event; | |
188 event->event.key.keysym = keysym; | |
189 event->event.key.modifiers = mods; | |
190 mswindows_enqueue_dispatch_event (emacs_event); | |
191 LeaveCriticalSection (&mswindows_dispatch_crit); | |
192 return (0); | |
193 } | |
194 } | |
195 } | |
196 TranslateMessage (&msg); /* Maybe generates WM_[SYS]CHAR in message queue */ | |
197 goto defproc; | |
198 | |
199 case WM_KEYUP: | |
200 case WM_SYSKEYUP: | |
201 switch(wParam) | |
202 { | |
203 case VK_SHIFT: | |
204 mods &= ~MOD_SHIFT; | |
205 break; | |
206 case VK_CONTROL: | |
207 mods &= ~MOD_CONTROL; | |
208 break; | |
209 case VK_MENU: | |
210 mods &= ~MOD_META; | |
211 break; | |
212 } | |
213 TranslateMessage (&msg); | |
214 goto defproc; | |
215 | |
216 case WM_CHAR: | |
217 case WM_SYSCHAR: | |
218 { | |
219 EnterCriticalSection (&mswindows_dispatch_crit); | |
220 emacs_event = Fmake_event (Qnil, Qnil); | |
221 event = XEVENT(emacs_event); | |
222 | |
223 event->channel = mswindows_find_console(hwnd); | |
224 event->timestamp = msg.time; | |
225 event->event_type = key_press_event; | |
226 event->event.key.modifiers = mods; | |
227 event->event.key.modifiers = lParam & 0x20000000 ? MOD_META : 0; /* redundant? */ | |
228 if (wParam<' ') /* Control char not handled under WM_KEYDOWN */ | |
229 { | |
230 event->event.key.keysym = make_char(wParam+'a'-1); | |
231 event->event.key.modifiers |= MOD_CONTROL; /* redundant? */ | |
232 } | |
233 else | |
234 { | |
235 /* Assumes that emacs keysym == ASCII code */ | |
236 event->event.key.keysym = make_char(wParam); | |
237 } | |
238 mswindows_enqueue_dispatch_event (emacs_event); | |
239 LeaveCriticalSection (&mswindows_dispatch_crit); | |
240 } | |
241 break; | |
242 | |
243 case WM_LBUTTONDOWN: | |
244 case WM_MBUTTONDOWN: | |
245 case WM_RBUTTONDOWN: | |
246 case WM_LBUTTONUP: | |
247 case WM_MBUTTONUP: | |
248 case WM_RBUTTONUP: | |
249 { | |
250 /* XXX FIXME: Do middle button emulation */ | |
251 EnterCriticalSection (&mswindows_dispatch_crit); | |
252 emacs_event = Fmake_event (Qnil, Qnil); | |
253 event = XEVENT(emacs_event); | |
254 | |
255 event->channel = mswindows_find_frame(hwnd); | |
256 event->timestamp = msg.time; | |
257 event->event_type = | |
258 (message==WM_LBUTTONDOWN || message==WM_MBUTTONDOWN || | |
259 message==WM_RBUTTONDOWN) ? | |
260 button_press_event : button_release_event; | |
261 #if 0 | |
262 ((wParam & MK_CONTROL) ? MOD_CONTROL : 0) | | |
263 ((wParam & MK_SHIFT) ? MOD_SHIFT : 0); | |
264 #endif | |
265 event->event.button.button = | |
266 (message==WM_LBUTTONDOWN || message==WM_LBUTTONUP) ? 1 : | |
267 ((message==WM_RBUTTONDOWN || message==WM_RBUTTONUP) ? 3 : 2); | |
268 event->event.button.x = LOWORD(lParam); | |
269 event->event.button.y = HIWORD(lParam); | |
270 event->event.button.modifiers = mods; | |
271 | |
272 mswindows_enqueue_dispatch_event (emacs_event); | |
273 LeaveCriticalSection (&mswindows_dispatch_crit); | |
274 } | |
275 break; | |
276 | |
277 case WM_MOUSEMOVE: | |
278 { | |
279 EnterCriticalSection (&mswindows_dispatch_crit); | |
280 emacs_event = Fmake_event (Qnil, Qnil); | |
281 event = XEVENT(emacs_event); | |
282 | |
283 event->channel = mswindows_find_frame(hwnd); | |
284 event->timestamp = msg.time; | |
285 event->event_type = pointer_motion_event; | |
286 event->event.motion.x = LOWORD(lParam); | |
287 event->event.motion.y = HIWORD(lParam); | |
288 event->event.motion.modifiers = mods; | |
289 | |
290 mswindows_enqueue_dispatch_event (emacs_event); | |
291 LeaveCriticalSection (&mswindows_dispatch_crit); | |
292 } | |
293 break; | |
294 | |
295 case WM_PAINT: | |
296 if (GetUpdateRect(hwnd, NULL, FALSE)) | |
297 { | |
298 PAINTSTRUCT paintStruct; | |
299 | |
300 EnterCriticalSection (&mswindows_dispatch_crit); | |
301 emacs_event = Fmake_event (Qnil, Qnil); | |
302 event = XEVENT(emacs_event); | |
303 | |
304 event->channel = mswindows_find_frame(hwnd); | |
305 event->timestamp = msg.time; | |
306 event->event_type = magic_event; | |
307 BeginPaint (hwnd, &paintStruct); | |
308 EVENT_MSWINDOWS_MAGIC_TYPE(event) = message; | |
309 EVENT_MSWINDOWS_MAGIC_DATA(event) = paintStruct.rcPaint; | |
310 EndPaint (hwnd, &paintStruct); | |
311 | |
312 mswindows_enqueue_dispatch_event (emacs_event); | |
313 LeaveCriticalSection (&mswindows_dispatch_crit); | |
314 } | |
315 break; | |
316 | |
317 case WM_SIZE: | |
318 /* We only care about this message if our size has really changed */ | |
319 if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED) | |
320 { | |
321 RECT rect; | |
322 EnterCriticalSection (&mswindows_dispatch_crit); | |
323 emacs_event = Fmake_event (Qnil, Qnil); | |
324 event = XEVENT(emacs_event); | |
325 | |
326 event->channel = mswindows_find_frame(hwnd); | |
327 event->timestamp = msg.time; | |
328 event->event_type = magic_event; | |
329 if (wParam==SIZE_MINIMIZED) | |
330 rect.left = rect.top = rect.right = rect.bottom = -1; | |
331 else | |
332 GetClientRect(hwnd, &rect); | |
333 EVENT_MSWINDOWS_MAGIC_TYPE(event) = message; | |
334 EVENT_MSWINDOWS_MAGIC_DATA(event) = rect; | |
335 | |
336 mswindows_enqueue_dispatch_event (emacs_event); | |
337 LeaveCriticalSection (&mswindows_dispatch_crit); | |
338 } | |
339 break; | |
340 | |
341 case WM_SETFOCUS: | |
342 case WM_KILLFOCUS: | |
343 { | |
344 EnterCriticalSection (&mswindows_dispatch_crit); | |
345 emacs_event = Fmake_event (Qnil, Qnil); | |
346 event = XEVENT(emacs_event); | |
347 | |
348 event->channel = mswindows_find_frame(hwnd); | |
349 event->timestamp = msg.time; | |
350 event->event_type = magic_event; | |
351 EVENT_MSWINDOWS_MAGIC_TYPE(event) = message; | |
352 | |
353 mswindows_enqueue_dispatch_event (emacs_event); | |
354 LeaveCriticalSection (&mswindows_dispatch_crit); | |
355 } | |
356 break; | |
357 | |
358 case WM_QUIT: | |
359 /* XXX FIXME: Should do something here! */ | |
360 defproc: | |
361 default: | |
362 return DefWindowProc (hwnd, message, wParam, lParam); | |
363 } | |
364 return (0); | |
365 } | |
366 | |
367 | |
368 /* | |
369 * Make a request to the message-processing thread to do things that | |
370 * can't be done in the main thread. | |
371 */ | |
372 LPARAM | |
373 mswindows_make_request(UINT message, WPARAM wParam, mswindows_request_type *request) | |
374 { | |
375 MSG msg; | |
376 assert(PostThreadMessage (mswindows_win_thread_id, message, wParam, | |
377 (LPARAM) request)); | |
378 GetMessage (&msg, NULL, WM_XEMACS_ACK, WM_XEMACS_ACK); | |
379 return (msg.lParam); | |
380 } | |
381 | |
382 | |
383 /* | |
384 * Handle a request from the main thread to do things that have to be | |
385 * done in the message-processing thread. | |
386 */ | |
387 static void | |
388 mswindows_handle_request (MSG *msg) | |
389 { | |
390 mswindows_request_type *request = (mswindows_request_type *) msg->lParam; | |
391 | |
392 switch (msg->message) | |
393 { | |
394 case WM_XEMACS_CREATEWINDOW: | |
395 { | |
396 struct frame *f = request->thing1; | |
397 Lisp_Object *props = request->thing2; | |
398 Lisp_Object name, height, width, popup, top, left; | |
399 RECT rect; | |
400 DWORD style; | |
401 HWND hwnd; | |
402 | |
403 name = Fplist_get (*props, Qname, Qnil); | |
404 height = Fplist_get (*props, Qheight, Qnil); | |
405 width = Fplist_get (*props, Qwidth, Qnil); | |
406 popup = Fplist_get (*props, Qpopup, Qnil); | |
407 top = Fplist_get (*props, Qtop, Qnil); | |
408 left = Fplist_get (*props, Qleft, Qnil); | |
409 | |
410 style = (NILP(popup)) ? MSWINDOWS_FRAME_STYLE : MSWINDOWS_POPUP_STYLE; | |
411 | |
412 rect.left = rect.top = 0; | |
413 rect.right = INTP(width) ? XINT(width) : 640; | |
414 rect.bottom = INTP(height) ? XINT(height) : 480; | |
415 #ifdef HAVE_MENUBARS | |
416 AdjustWindowRect(&rect, style, TRUE); | |
417 #else | |
418 AdjustWindowRect(&rect, style, FALSE); | |
419 #endif | |
420 | |
421 hwnd = CreateWindow (XEMACS_CLASS, | |
422 STRINGP(f->name) ? XSTRING_DATA(f->name) : | |
423 (STRINGP(name) ? XSTRING_DATA(name) : XEMACS_CLASS), | |
424 style, | |
425 INTP(left) ? XINT(left) : CW_USEDEFAULT, | |
426 INTP(top) ? XINT(top) : CW_USEDEFAULT, | |
427 rect.right-rect.left, rect.bottom-rect.top, | |
428 NULL, NULL, NULL, NULL); | |
429 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, (LPARAM) hwnd)); | |
430 } | |
431 return; | |
432 | |
433 case WM_XEMACS_SETTIMER: | |
434 { | |
435 UINT id; | |
436 id=SetTimer (NULL, 0, (UINT) request->thing1, NULL); | |
437 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, id)); | |
438 } | |
439 break; | |
440 | |
441 case WM_XEMACS_KILLTIMER: | |
442 { | |
443 KillTimer (NULL, (UINT) request->thing1); | |
444 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, 0)); | |
445 } | |
446 break; | |
447 | |
448 default: | |
449 assert(0); | |
450 } | |
451 } | |
452 | |
453 | |
454 /* | |
455 * Translate a mswindows virtual key to a keysym. | |
456 * Only returns non-Qnil for keys that don't generate WM_CHAR messages | |
457 * or whose ASCII codes (like space) xemacs doesn't like. | |
458 * Virtual key values are defined in winresrc.h | |
459 * XXX I'm not sure that KEYSYM("name") is the best thing to use here. | |
460 */ | |
461 Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key) | |
462 { | |
463 switch (mswindows_key) | |
464 { | |
465 /* First the predefined ones */ | |
466 case VK_BACK: return QKbackspace; | |
467 case VK_TAB: return QKtab; | |
468 case '\n': return QKlinefeed; /* No VK_LINEFEED in winresrc.h */ | |
469 case VK_RETURN: return QKreturn; | |
470 case VK_ESCAPE: return QKescape; | |
471 case VK_SPACE: return QKspace; | |
472 case VK_DELETE: return QKdelete; | |
473 | |
474 /* The rest */ | |
475 case VK_PRIOR: return KEYSYM ("prior"); | |
476 case VK_NEXT: return KEYSYM ("next"); | |
477 case VK_END: return KEYSYM ("end"); | |
478 case VK_HOME: return KEYSYM ("home"); | |
479 case VK_LEFT: return KEYSYM ("left"); | |
480 case VK_UP: return KEYSYM ("up"); | |
481 case VK_RIGHT: return KEYSYM ("right"); | |
482 case VK_DOWN: return KEYSYM ("down"); | |
483 case VK_INSERT: return KEYSYM ("insert"); | |
484 case VK_HELP: return KEYSYM ("help"); | |
485 case VK_F1: return KEYSYM ("F1"); | |
486 case VK_F2: return KEYSYM ("F2"); | |
487 case VK_F3: return KEYSYM ("F3"); | |
488 case VK_F4: return KEYSYM ("F4"); | |
489 case VK_F5: return KEYSYM ("F5"); | |
490 case VK_F6: return KEYSYM ("F6"); | |
491 case VK_F7: return KEYSYM ("F7"); | |
492 case VK_F8: return KEYSYM ("F8"); | |
493 case VK_F9: return KEYSYM ("F9"); | |
494 case VK_F10: return KEYSYM ("F10"); | |
495 case VK_F11: return KEYSYM ("F11"); | |
496 case VK_F12: return KEYSYM ("F12"); | |
497 case VK_F13: return KEYSYM ("F13"); | |
498 case VK_F14: return KEYSYM ("F14"); | |
499 case VK_F15: return KEYSYM ("F15"); | |
500 case VK_F16: return KEYSYM ("F16"); | |
501 case VK_F17: return KEYSYM ("F17"); | |
502 case VK_F18: return KEYSYM ("F18"); | |
503 case VK_F19: return KEYSYM ("F19"); | |
504 case VK_F20: return KEYSYM ("F20"); | |
505 case VK_F21: return KEYSYM ("F21"); | |
506 case VK_F22: return KEYSYM ("F22"); | |
507 case VK_F23: return KEYSYM ("F23"); | |
508 case VK_F24: return KEYSYM ("F24"); | |
509 } | |
510 return Qnil; | |
511 } | |
512 | |
513 | |
514 /* | |
515 * Find the console that matches the supplied mswindows window handle | |
516 */ | |
517 static Lisp_Object | |
518 mswindows_find_console (HWND hwnd) | |
519 { | |
520 Lisp_Object concons; | |
521 | |
522 CONSOLE_LOOP (concons) | |
523 { | |
524 Lisp_Object console = XCAR (concons); | |
525 /* We only support one console so this must be it */ | |
526 return console; | |
527 } | |
528 | |
529 return Qnil; | |
530 } | |
531 | |
532 /* | |
533 * Find the frame that matches the supplied mswindows window handle | |
534 */ | |
535 static Lisp_Object | |
536 mswindows_find_frame (HWND hwnd) | |
537 { | |
538 Lisp_Object frmcons, devcons, concons; | |
539 | |
540 FRAME_LOOP_NO_BREAK (frmcons, devcons, concons) | |
541 { | |
542 struct frame *f; | |
543 Lisp_Object frame = XCAR (frmcons); | |
544 f = XFRAME (frame); | |
545 if (FRAME_TYPE_P(f, mswindows)) /* Might be a stream-type frame */ | |
546 if (FRAME_MSWINDOWS_HANDLE(f)==hwnd) | |
547 return frame; | |
548 } | |
549 assert(0); /* XXX Can't happen! we only get messages for our windows */ | |
550 return Qnil; | |
551 } | |
552 | |
553 /* | |
554 * Random helper functions for debugging. | |
555 * Intended for use in the MSVC "Watch" window which doesn't like | |
556 * the aborts that the error_check_foo() functions can make. | |
557 */ | |
558 struct lrecord_header *DHEADER(Lisp_Object obj) | |
559 { | |
560 return LRECORDP(obj) ? XRECORD_LHEADER(obj) : NULL; | |
561 /* (lrecord_header*)(obj & 0xfffffff) */ | |
562 } | |
563 | |
564 struct Lisp_Event *DEVENT(Lisp_Object obj) | |
565 { | |
566 return (EVENTP (obj)) ? XEVENT(obj) : NULL; | |
567 } | |
568 | |
569 struct Lisp_Cons *DCONS(Lisp_Object obj) | |
570 { | |
571 return (CONSP (obj)) ? XCONS(obj) : NULL; | |
572 } | |
573 | |
574 Lisp_Object DCAR(Lisp_Object obj) | |
575 { | |
576 return (CONSP (obj)) ? XCAR(obj) : 0; | |
577 } | |
578 | |
579 Lisp_Object DCDR(Lisp_Object obj) | |
580 { | |
581 return (CONSP (obj)) ? XCDR(obj) : 0; | |
582 } | |
583 | |
584 char *DSTRING(Lisp_Object obj) | |
585 { | |
586 return (STRINGP (obj)) ? XSTRING_DATA(obj) : NULL; | |
587 } | |
588 | |
589 struct Lisp_Vector *DVECTOR(Lisp_Object obj) | |
590 { | |
591 return (VECTORP (obj)) ? XVECTOR(obj) : NULL; | |
592 } | |
593 | |
594 struct Lisp_Symbol *DSYMBOL(Lisp_Object obj) | |
595 { | |
596 return (SYMBOLP (obj)) ? XSYMBOL(obj) : NULL; | |
597 } | |
598 | |
599 char *DSYMNAME(Lisp_Object obj) | |
600 { | |
601 return (SYMBOLP (obj)) ? XSYMBOL(obj)->name->_data : NULL; | |
602 } |