1/*
2   XGServerEvent - Window/Event code for X11 backends.
3
4   Copyright (C) 1998-2015 Free Software Foundation, Inc.
5
6   Written by:  Adam Fedor <fedor@boulder.colorado.edu>
7   Date: Nov 1998
8
9   This file is part of the GNU Objective C User Interface Library.
10
11   This library is free software; you can redistribute it and/or
12   modify it under the terms of the GNU Lesser General Public
13   License as published by the Free Software Foundation; either
14   version 2 of the License, or (at your option) any later version.
15
16   This library is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
19   Lesser General Public License for more details.
20
21   You should have received a copy of the GNU Lesser General Public
22   License along with this library; see the file COPYING.LIB.
23   If not, see <http://www.gnu.org/licenses/> or write to the
24   Free Software Foundation, 51 Franklin Street, Fifth Floor,
25*/
26
27#include "config.h"
28
29#include <AppKit/AppKitExceptions.h>
30#include <AppKit/NSApplication.h>
31#include <AppKit/NSGraphics.h>
32#include <AppKit/NSMenu.h>
33#include <AppKit/NSPasteboard.h>
34#include <AppKit/NSWindow.h>
35#include <AppKit/NSScreen.h>
36#include <Foundation/NSException.h>
37#include <Foundation/NSArray.h>
38#include <Foundation/NSDictionary.h>
39#include <Foundation/NSData.h>
40#include <Foundation/NSNotification.h>
41#include <Foundation/NSValue.h>
42#include <Foundation/NSString.h>
43#include <Foundation/NSUserDefaults.h>
44#include <Foundation/NSRunLoop.h>
45#include <Foundation/NSDebug.h>
46#include <Foundation/NSDistributedNotificationCenter.h>
47
48#include "x11/XGServerWindow.h"
49#include "x11/XGInputServer.h"
50#include "x11/XGDragView.h"
51#include "x11/XGGeneric.h"
52#include "x11/xdnd.h"
53
54#ifdef HAVE_WRASTER_H
55#include "wraster.h"
56#else
57#include "x11/wraster.h"
58#endif
59#ifdef HAVE_XRANDR
60#include <X11/extensions/Xrandr.h>
61#endif
62
63#include "math.h"
64#include <X11/keysym.h>
65#include <X11/Xproto.h>
66
67#if LIB_FOUNDATION_LIBRARY
68# include <Foundation/NSPosixFileDescriptor.h>
69#elif defined(NeXT_PDO)
70# include <Foundation/NSFileHandle.h>
71# include <Foundation/NSNotification.h>
72#endif
73
74#define cWin ((gswindow_device_t*)generic.cachedWindow)
75
76// NumLock's mask (it depends on the keyboard mapping)
77static unsigned int _num_lock_mask;
78// Modifier state
79static char _shift_pressed = 0;
80static char _control_pressed = 0;
81static char _command_pressed = 0;
82static char _alt_pressed = 0;
83static char _help_pressed = 0;
84/*
85Keys used for the modifiers (you may set them with user preferences).
86Note that the first and second key sym for a modifier must be different.
87Otherwise, the _*_pressed tracking will be confused.
88*/
89static KeySym _control_keysyms[2];
90static KeySym _command_keysyms[2];
91static KeySym _alt_keysyms[2];
92static KeySym _help_keysyms[2];
93
94static BOOL _is_keyboard_initialized = NO;
95static BOOL _mod_ignore_shift = NO;
96
97/*
98  Mouse properties. In comments below specified defaults key and default value.
99*/
100static NSInteger   clickTime;             // "GSDoubleClickTime" - milisecond (250)
101static NSInteger   clickMove;             // "GSMouseMoveThreshold" - in pixels (3)
102static NSInteger   mouseScrollMultiplier; // "GSMouseScrollMultiplier" - times (1)
103static NSEventType menuMouseButton;       // "GSMenuButtonEvent" - (NSRightMouseButon)
104static BOOL        menuButtonEnabled;     // "GSMenuButtonEnabled" - BOOL
105static BOOL        swapMouseButtons;      // YES if "GSMenuButtonEvent" == NSLeftMouseButton
106
107void __objc_xgcontextevent_linking (void)
108{
109}
110
111static SEL procSel = 0;
112static void (*procEvent)(id, SEL, XEvent*) = 0;
113
114#ifdef XSHM
115@interface NSGraphicsContext (SharedMemory)
116-(void) gotShmCompletion: (Drawable)d;
117@end
118#endif
119
120@interface XGServer (Private)
121- (void) receivedEvent: (void*)data
122                  type: (RunLoopEventType)type
123                 extra: (void*)extra
124               forMode: (NSString*)mode;
125- (void) setupRunLoopInputSourcesForMode: (NSString*)mode;
126- (NSDate*) timedOutEvent: (void*)data
127                     type: (RunLoopEventType)type
128                  forMode: (NSString*)mode;
129- (int) XGErrorHandler: (Display*)display : (XErrorEvent*)err;
130- (void) processEvent: (XEvent *) event;
131- (NSEvent *)_handleTakeFocusAtom: (XEvent)xEvent
132        	       forContext: (NSGraphicsContext *)gcontext;
133@end
134
135
136int
137XGErrorHandler(Display *display, XErrorEvent *err)
138{
139  XGServer *ctxt = (XGServer*)GSCurrentServer();
140
141  return [ctxt XGErrorHandler: display : err];
142}
143
144static NSEvent*process_key_event (XEvent* xEvent, XGServer* ctxt,
145                                  NSEventType eventType,
146                                  NSMutableArray *event_queue, BOOL keyRepeat);
147
148static unichar process_char (KeySym keysym, unsigned *eventModifierFlags);
149
150static unsigned process_modifier_flags(unsigned int state);
151
152static void initialize_keyboard (void);
153
154static void set_up_num_lock (void);
155
156// checks whether a GNUstep modifier (key_sym) is pressed when we're only able
157// to check whether X keycodes are pressed in xEvent->xkeymap;
158static int check_modifier (XEvent *xEvent, KeySym key_sym)
159{
160  char *key_vector;
161  int by,bi;
162  int key_code = XKeysymToKeycode(xEvent->xkeymap.display, key_sym);
163
164  if (key_code != NoSymbol)
165    {
166      by = key_code / 8;
167      bi = key_code % 8;
168      key_vector = xEvent->xkeymap.key_vector;
169      return (key_vector[by] & (1 << bi));
170    }
171  return 0;
172}
173
174@interface XGServer (WindowOps)
175- (void) styleoffsets: (float *) l : (float *) r : (float *) t : (float *) b
176                     : (unsigned int) style : (Window) win;
177- (NSRect) _XWinRectToOSWinRect: (NSRect)r for: (void*)windowNumber;
178@end
179
180@implementation XGServer (EventOps)
181
182- (int) XGErrorHandler: (Display*)display : (XErrorEvent*)err
183{
184  int length = 1024;
185  char buffer[length+1];
186
187  /*
188   * Ignore attempts to set input focus to unmapped window, except for noting
189   * if the most recent request failed (mark the request serial number to 0)
190   * in which case we should repeat the request when the window becomes
191   * mapped again.
192   */
193  if (err->error_code == BadMatch && err->request_code == X_SetInputFocus)
194    {
195      if (err->serial == generic.focusRequestNumber)
196        {
197          generic.focusRequestNumber = 0;
198        }
199      return 0;
200    }
201
202  XGetErrorText(display, err->error_code, buffer, length);
203  if (err->type == 0
204      && GSDebugSet(@"XSynchronize") == NO)
205    {
206      NSLog(@"X-Windows error - %s\n\
207          on display: %s\n\
208                type: %d\n\
209       serial number: %lu\n\
210        request code: %d\n",
211        buffer,
212        XDisplayName(DisplayString(display)),
213        err->type, err->serial, err->request_code);
214      return 0;
215    }
216  [NSException raise: NSWindowServerCommunicationException
217    format: @"X-Windows error - %s\n\
218          on display: %s\n\
219                type: %d\n\
220       serial number: %lu\n\
221        request code: %d\n",
222        buffer,
223        XDisplayName(DisplayString(display)),
224        err->type, err->serial, err->request_code];
225  return 0;
226}
227
228- (void) setupRunLoopInputSourcesForMode: (NSString*)mode
229{
230  int xEventQueueFd = XConnectionNumber(dpy);
231  NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
232
233#if defined(LIB_FOUNDATION_LIBRARY)
234  {
235    id fileDescriptor = [[[NSPosixFileDescriptor alloc]
236        initWithFileDescriptor: xEventQueueFd]
237        autorelease];
238
239    // Invoke limitDateForMode: to setup the current
240    // mode of the run loop (the doc says that this
241    // method and acceptInputForMode: beforeDate: are
242    // the only ones that setup the current mode).
243
244    [currentRunLoop limitDateForMode: mode];
245
246    [fileDescriptor setDelegate: self];
247    [fileDescriptor monitorFileActivity: NSPosixReadableActivity];
248  }
249#elif defined(NeXT_PDO)
250  {
251    id fileDescriptor = [[[NSFileHandle alloc]
252        initWithFileDescriptor: xEventQueueFd]
253        autorelease];
254
255    [[NSNotificationCenter defaultCenter] addObserver: self
256        selector: @selector(activityOnFileHandle:)
257        name: NSFileHandleDataAvailableNotification
258        object: fileDescriptor];
259    [fileDescriptor waitForDataInBackgroundAndNotifyForModes:
260        [NSArray arrayWithObject: mode]];
261  }
262#else
263  [currentRunLoop addEvent: (void*)(gsaddr)xEventQueueFd
264                      type: ET_RDESC
265                   watcher: (id<RunLoopEvents>)self
266                   forMode: mode];
267#endif
268  if (procSel == 0)
269    {
270      procSel = @selector(processEvent:);
271      procEvent = (void (*)(id, SEL, XEvent*))
272        [self methodForSelector: procSel];
273    }
274}
275
276#if LIB_FOUNDATION_LIBRARY
277- (void) activity: (NSPosixFileActivities)activity
278posixFileDescriptor: (NSPosixFileDescriptor*)fileDescriptor
279{
280  [self receivedEvent: 0 type: 0 extra: 0 forMode: nil];
281}
282#elif defined(NeXT_PDO)
283- (void) activityOnFileHandle: (NSNotification*)notification
284{
285  id fileDescriptor = [notification object];
286  id runLoopMode = [[NSRunLoop currentRunLoop] currentMode];
287
288  [fileDescriptor waitForDataInBackgroundAndNotifyForModes:
289        [NSArray arrayWithObject: runLoopMode]];
290  [self receivedEvent: 0 type: 0 extra: 0 forMode: nil];
291}
292#endif
293
294- (BOOL) runLoopShouldBlock: (BOOL*)trigger
295{
296  *trigger = YES;        //  Should trigger this event
297  if (XPending(dpy) > 0)
298    {
299      return NO;        // Don't block
300    }
301  return YES;
302}
303
304- (void) receivedEvent: (void*)data
305                  type: (RunLoopEventType)type
306                 extra: (void*)extra
307               forMode: (NSString*)mode
308{
309  XEvent xEvent;
310
311  // loop and grab all of the events from the X queue
312  while (XPending(dpy) > 0)
313    {
314      XNextEvent(dpy, &xEvent);
315
316#ifdef USE_XIM
317      if (XFilterEvent(&xEvent, None))
318        {
319          NSDebugLLog(@"NSKeyEvent", @"Event filtered (by XIM?)\n");
320          continue;
321        }
322#endif
323
324      (*procEvent)(self, procSel, &xEvent);
325    }
326}
327
328/*
329 */
330- (NSPoint) _XPointToOSPoint: (NSPoint)x for: (void*)window
331{
332  gswindow_device_t *win = (gswindow_device_t*)window;
333  unsigned int style = win->win_attrs.window_style;
334  NSPoint o;
335  float t, b, l, r;
336
337  [self styleoffsets: &l : &r : &t : &b : style : win->ident];
338  o.x = x.x + l;
339  o.y = NSHeight(win->xframe) - x.y + b;
340
341  NSDebugLLog(@"Frame", @"X2OP %lu, %x, %@, %@", win->number, style,
342    NSStringFromPoint(x), NSStringFromPoint(o));
343  return o;
344}
345
346- (void) mouseOptionsChanged: (NSNotification *)aNotif
347{
348  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
349
350  clickTime = [defs integerForKey:@"GSDoubleClickTime"];
351  if (clickTime < 200)
352    clickTime = 300;
353
354  clickMove = [defs integerForKey:@"GSMouseMoveThreshold"];
355  if (clickMove < 3)
356    clickMove = 3;
357
358  mouseScrollMultiplier = [defs integerForKey:@"GSMouseScrollMultiplier"];
359  if (mouseScrollMultiplier == 0)
360    mouseScrollMultiplier = 1;
361
362  if ([defs objectForKey:@"GSMenuButtonEnabled"])
363    menuButtonEnabled = [defs boolForKey:@"GSMenuButtonEnabled"];
364  else
365    menuButtonEnabled = YES;
366
367  if ([defs objectForKey:@"GSMenuButtonEvent"])
368    menuMouseButton = [defs integerForKey:@"GSMenuButtonEvent"];
369  else
370    menuMouseButton = NSRightMouseDown;
371
372  switch (menuMouseButton)
373    {
374    case NSLeftMouseDown:
375      swapMouseButtons = YES;
376      break;
377    default:
378      swapMouseButtons = NO;
379      break;
380    }
381}
382
383- (void) initializeMouse
384{
385  [self mouseOptionsChanged: nil];
386  [[NSDistributedNotificationCenter defaultCenter]
387    addObserver: self
388       selector: @selector(mouseOptionsChanged:)
389           name: NSUserDefaultsDidChangeNotification
390         object: nil];
391}
392
393- (void) processEvent: (XEvent *) event
394{
395  static int clickCount = 1;
396  static unsigned int eventFlags;
397  static NSPoint eventLocation;
398  NSEvent *e = nil;
399  XEvent xEvent;
400  NSWindow *nswin;
401  Window xWin;
402  NSEventType eventType;
403  NSGraphicsContext *gcontext;
404  float deltaX;
405  float deltaY;
406  int buttonNumber;
407
408  gcontext = GSCurrentContext();
409  xEvent = *event;
410
411  switch (xEvent.type)
412    {
413        // mouse button events
414      case ButtonPress:
415        NSDebugLLog(@"NSEvent", @"%lu ButtonPress: \
416                xEvent.xbutton.time %lu timeOfLastClick %lu \n",
417                    xEvent.xbutton.window, xEvent.xbutton.time,
418                    generic.lastClick);
419        /*
420         * hardwired test for a double click
421         *
422         * For multiple clicks, the clicks must remain in the same
423         * region of the same window and must occur in a limited time.
424         *
425         * default time of 300 should be user set;
426         * perhaps the movement of 3 should also be a preference?
427         */
428        {
429          BOOL incrementCount = YES;
430
431          if (clickTime == 0) [self initializeMouse];
432
433          if (xEvent.xbutton.time
434            >= (unsigned long)(generic.lastClick + clickTime))
435            incrementCount = NO;
436          else if (generic.lastClickWindow != xEvent.xbutton.window)
437            incrementCount = NO;
438          else if ((generic.lastClickX - xEvent.xbutton.x) > clickMove)
439            incrementCount = NO;
440          else if ((generic.lastClickX - xEvent.xbutton.x) < -clickMove)
441            incrementCount = NO;
442          else if ((generic.lastClickY - xEvent.xbutton.y) > clickMove)
443            incrementCount = NO;
444          else if ((generic.lastClickY - xEvent.xbutton.y) < -clickMove)
445            incrementCount = NO;
446
447          if (incrementCount == YES)
448            {
449              clickCount++;
450            }
451          else
452            {
453              /*
454               * Not a multiple-click, so we must set the stored
455               * location of the click to the new values and
456               * reset the counter.
457               */
458              clickCount = 1;
459              generic.lastClickWindow = xEvent.xbutton.window;
460              generic.lastClickX = xEvent.xbutton.x;
461              generic.lastClickY = xEvent.xbutton.y;
462            }
463        }
464        generic.lastClick = xEvent.xbutton.time;
465        [self setLastTime: generic.lastClick];
466	deltaX = 0.0;
467        deltaY = 0.0;
468
469        if (xEvent.xbutton.button == generic.lMouse)
470          {
471            if (swapMouseButtons)
472              {
473                eventType = NSRightMouseDown;
474                buttonNumber = generic.rMouse;
475              }
476            else
477              {
478                eventType = NSLeftMouseDown;
479                buttonNumber = generic.lMouse;
480              }
481          }
482        else if (xEvent.xbutton.button == generic.rMouse
483                 && generic.rMouse != 0)
484          {
485            if (swapMouseButtons)
486              {
487                eventType = NSLeftMouseDown;
488                buttonNumber = generic.lMouse;
489              }
490            else
491              {
492                eventType = NSRightMouseDown;
493                buttonNumber = generic.rMouse;
494              }
495          }
496        else if (xEvent.xbutton.button == generic.mMouse
497                 && generic.mMouse != 0)
498          {
499            eventType = NSOtherMouseDown;
500            buttonNumber = generic.mMouse;
501          }
502        else if (xEvent.xbutton.button == generic.upMouse
503                 && generic.upMouse != 0)
504          {
505            deltaY = 1. * mouseScrollMultiplier;
506            eventType = NSScrollWheel;
507            buttonNumber = generic.upMouse;
508          }
509        else if (xEvent.xbutton.button == generic.downMouse
510                 && generic.downMouse != 0)
511          {
512            deltaY = -1. * mouseScrollMultiplier;
513            eventType = NSScrollWheel;
514            buttonNumber = generic.downMouse;
515          }
516        else if (xEvent.xbutton.button == generic.scrollLeftMouse
517                 && generic.scrollLeftMouse != 0)
518          {
519            deltaX = -1. * mouseScrollMultiplier;
520            eventType = NSScrollWheel;
521            buttonNumber = generic.scrollLeftMouse;
522          }
523        else if (xEvent.xbutton.button == generic.scrollRightMouse
524                 && generic.scrollRightMouse != 0)
525          {
526            deltaX = 1. * mouseScrollMultiplier;
527            eventType = NSScrollWheel;
528            buttonNumber = generic.scrollRightMouse;
529          }
530        else
531          {
532            break;                /* Unknown button */
533          }
534
535        if (menuButtonEnabled == NO && eventType == menuMouseButton)
536          break; // disabled menu button was pressed
537
538        eventFlags = process_modifier_flags(xEvent.xbutton.state);
539        // if pointer is grabbed use grab window
540        xWin = (grabWindow == 0) ? xEvent.xbutton.window : grabWindow;
541        if (cWin == 0 || xWin != cWin->ident)
542          generic.cachedWindow = [XGServer _windowForXWindow: xWin];
543        if (cWin == 0)
544          break;
545        eventLocation.x = xEvent.xbutton.x;
546        eventLocation.y = xEvent.xbutton.y;
547        eventLocation = [self _XPointToOSPoint: eventLocation
548                                           for: cWin];
549
550        if (generic.flags.useWindowMakerIcons == 1)
551          {
552            /*
553             * We must hand over control of our icon/miniwindow
554             * to Window Maker.
555             */
556            if ((cWin->win_attrs.window_style
557                 & (NSMiniWindowMask | NSIconWindowMask)) != 0
558                && eventType == NSLeftMouseDown)
559              {
560                if (cWin->parent == None)
561                  break;
562                xEvent.xbutton.window = cWin->parent;
563                XUngrabPointer(dpy, CurrentTime);
564                XSendEvent(dpy, cWin->parent, True,
565                           ButtonPressMask, &xEvent);
566                XFlush(dpy);
567                if (clickCount == 1)
568                  break;
569              }
570          }
571
572        // create NSEvent
573        e = [NSEvent mouseEventWithType: eventType
574                     location: eventLocation
575                     modifierFlags: eventFlags
576         timestamp: (NSTimeInterval)generic.lastClick / 1000.0
577                     windowNumber: cWin->number
578                     context: gcontext
579                     eventNumber: xEvent.xbutton.serial
580                     clickCount: clickCount
581                     pressure: 1.0
582                     buttonNumber: buttonNumber /* FIXME */
583                     deltaX: deltaX
584                     deltaY: deltaY
585                     deltaZ: 0.];
586        break;
587
588      case ButtonRelease:
589        NSDebugLLog(@"NSEvent", @"%lu ButtonRelease\n",
590                    xEvent.xbutton.window);
591        [self setLastTime: xEvent.xbutton.time];
592        if (xEvent.xbutton.button == generic.lMouse)
593          {
594            if (swapMouseButtons)
595              {
596                eventType = NSRightMouseUp;
597                buttonNumber = generic.rMouse;
598              }
599            else
600              {
601                eventType = NSLeftMouseUp;
602                buttonNumber = generic.lMouse;
603              }
604          }
605        else if (xEvent.xbutton.button == generic.rMouse
606                 && generic.rMouse != 0)
607          {
608            if (swapMouseButtons)
609              {
610                eventType = NSLeftMouseUp;
611                buttonNumber = generic.lMouse;
612              }
613            else
614              {
615                eventType = NSRightMouseUp;
616                buttonNumber = generic.rMouse;
617              }
618          }
619        else if (xEvent.xbutton.button == generic.mMouse
620                 && generic.mMouse != 0)
621          {
622            eventType = NSOtherMouseUp;
623            buttonNumber = generic.mMouse;
624          }
625        else
626          {
627            // we ignore release of scrollUp or scrollDown
628            break;                /* Unknown button */
629          }
630
631        if (menuButtonEnabled == NO && eventType == menuMouseButton)
632          break; // disabled menu button was released
633
634        eventFlags = process_modifier_flags(xEvent.xbutton.state);
635        // if pointer is grabbed use grab window
636        xWin = (grabWindow == 0) ? xEvent.xbutton.window : grabWindow;
637        if (cWin == 0 || xWin != cWin->ident)
638          generic.cachedWindow = [XGServer _windowForXWindow: xWin];
639        if (cWin == 0)
640          break;
641        eventLocation.x = xEvent.xbutton.x;
642        eventLocation.y = xEvent.xbutton.y;
643        eventLocation = [self _XPointToOSPoint: eventLocation
644                                           for: cWin];
645
646        e = [NSEvent mouseEventWithType: eventType
647                     location: eventLocation
648                     modifierFlags: eventFlags
649                     timestamp: (NSTimeInterval)generic.lastTime / 1000.0
650                     windowNumber: cWin->number
651                     context: gcontext
652                     eventNumber: xEvent.xbutton.serial
653                     clickCount: clickCount
654                     pressure: 1.0
655                     buttonNumber: buttonNumber        /* FIXMME */
656                     deltaX: 0.0
657                     deltaY: 0.0
658                     deltaZ: 0.0];
659        break;
660
661      case CirculateNotify:
662        NSDebugLLog(@"NSEvent", @"%lu CirculateNotify\n",
663                    xEvent.xcirculate.window);
664        break;
665
666      case CirculateRequest:
667        NSDebugLLog(@"NSEvent", @"%lu CirculateRequest\n",
668                    xEvent.xcirculaterequest.window);
669        break;
670
671      case ClientMessage:
672        {
673          NSTimeInterval time;
674          DndClass dnd = xdnd ();
675
676          NSDebugLLog(@"NSEvent", @"%lu ClientMessage - %s\n",
677	    xEvent.xclient.window,
678	    XGetAtomName(dpy, xEvent.xclient.message_type));
679          if (cWin == 0 || xEvent.xclient.window != cWin->ident)
680            {
681              generic.cachedWindow
682                = [XGServer _windowForXWindow: xEvent.xclient.window];
683            }
684          if (cWin == 0)
685            break;
686          if (xEvent.xclient.message_type == generic.WM_PROTOCOLS_ATOM)
687            {
688              [self setLastTime: (Time)xEvent.xclient.data.l[1]];
689              NSDebugLLog(@"NSEvent", @"WM Protocol - %s\n",
690                          XGetAtomName(dpy, xEvent.xclient.data.l[0]));
691
692              if ((Atom)xEvent.xclient.data.l[0] == generic.WM_DELETE_WINDOW_ATOM)
693                {
694                  /*
695                   * WM is asking us to close a window
696                   */
697                  eventLocation = NSMakePoint(0,0);
698                  e = [NSEvent otherEventWithType: NSAppKitDefined
699                               location: eventLocation
700                               modifierFlags: 0
701                               timestamp: 0
702                               windowNumber: cWin->number
703                               context: gcontext
704                               subtype: GSAppKitWindowClose
705                               data1: 0
706                               data2: 0];
707                }
708              else if ((Atom)xEvent.xclient.data.l[0]
709                == generic._GNUSTEP_WM_MINIATURIZE_WINDOW_ATOM)
710                {
711		  NSDebugLLog(@"Miniaturize", @"%lu miniaturized", cWin->number);
712                  eventLocation = NSMakePoint(0,0);
713                  e = [NSEvent otherEventWithType: NSAppKitDefined
714                               location: eventLocation
715                               modifierFlags: 0
716                               timestamp: 0
717                               windowNumber: cWin->number
718                               context: gcontext
719                               subtype: GSAppKitWindowMiniaturize
720                               data1: 0
721                               data2: 0];
722                }
723              else if ((Atom)xEvent.xclient.data.l[0]
724                       == generic._GNUSTEP_WM_HIDE_APP_ATOM)
725                {
726                  NSDebugLLog(@"Hide", @"%lu application will be hidden", cWin->number);
727                  eventLocation = NSMakePoint(0,0);
728                  e = [NSEvent otherEventWithType: NSAppKitDefined
729                                         location: eventLocation
730                                    modifierFlags: 0
731                                        timestamp: 0
732                                     windowNumber: cWin->number
733                                          context: gcontext
734                                          subtype: GSAppKitAppHide
735                                            data1: 0
736                                            data2: 0];
737                }
738              else if ((Atom)xEvent.xclient.data.l[0]
739                == generic.WM_TAKE_FOCUS_ATOM)
740                {
741                  e = [self _handleTakeFocusAtom: xEvent
742                                      forContext: gcontext];
743                }
744              else if ((Atom)xEvent.xclient.data.l[0]
745                == generic._NET_WM_PING_ATOM)
746                {
747                  xEvent.xclient.window = RootWindow(dpy, cWin->screen_id);
748                  XSendEvent(dpy, xEvent.xclient.window, False,
749                    (SubstructureRedirectMask | SubstructureNotifyMask),
750                    &xEvent);
751                }
752#ifdef HAVE_X11_EXTENSIONS_SYNC_H
753	      else if ((Atom)xEvent.xclient.data.l[0]
754		== generic._NET_WM_SYNC_REQUEST_ATOM)
755		{
756		  cWin->net_wm_sync_request_counter_value_low = (Atom)xEvent.xclient.data.l[2];
757		  cWin->net_wm_sync_request_counter_value_high = (Atom)xEvent.xclient.data.l[3];
758		}
759#endif
760            }
761          else if (xEvent.xclient.message_type == dnd.XdndEnter)
762            {
763              Window source;
764
765              NSDebugLLog(@"NSDragging", @"  XdndEnter message\n");
766              source = XDND_ENTER_SOURCE_WIN(&xEvent);
767              eventLocation = NSMakePoint(0,0);
768              e = [NSEvent otherEventWithType: NSAppKitDefined
769                           location: eventLocation
770                           modifierFlags: 0
771                           timestamp: 0
772                           windowNumber: cWin->number
773                           context: gcontext
774                           subtype: GSAppKitDraggingEnter
775                           data1: source
776                           data2: 0];
777              /* If this is a non-local drag, set the dragInfo */
778              if ([XGServer _windowForXWindow: source] == NULL)
779                {
780                  [[XGDragView sharedDragView] setupDragInfoFromXEvent:
781                                                 &xEvent];
782                }
783            }
784          else if (xEvent.xclient.message_type == dnd.XdndPosition)
785            {
786              Window                source;
787              Atom                action;
788              NSDragOperation        operation;
789              int root_x, root_y;
790              Window root_child;
791
792              NSDebugLLog(@"NSDragging", @"  XdndPosition message\n");
793              source = XDND_POSITION_SOURCE_WIN(&xEvent);
794              /*
795                Work around a bug/feature in WindowMaker that does not
796                send ConfigureNotify events for app icons.
797              */
798              XTranslateCoordinates(dpy, xEvent.xclient.window,
799                                    RootWindow(dpy, cWin->screen_id),
800                                    0, 0,
801                                    &root_x, &root_y,
802                                    &root_child);
803              cWin->xframe.origin.x = root_x;
804              cWin->xframe.origin.y = root_y;
805
806              eventLocation.x = XDND_POSITION_ROOT_X(&xEvent) -
807                NSMinX(cWin->xframe);
808              eventLocation.y = XDND_POSITION_ROOT_Y(&xEvent) -
809                NSMinY(cWin->xframe);
810              eventLocation = [self _XPointToOSPoint: eventLocation
811                                                 for: cWin];
812              time = XDND_POSITION_TIME(&xEvent);
813              action = XDND_POSITION_ACTION(&xEvent);
814              operation = GSDragOperationForAction(action);
815              e = [NSEvent otherEventWithType: NSAppKitDefined
816                           location: eventLocation
817                           modifierFlags: 0
818                           timestamp: time / 1000.0
819                           windowNumber: cWin->number
820                           context: gcontext
821                           subtype: GSAppKitDraggingUpdate
822                           data1: source
823                           data2: operation];
824              /* If this is a non-local drag, update the dragInfo */
825              if ([XGServer _windowForXWindow: source] == NULL)
826                {
827                  [[XGDragView sharedDragView] updateDragInfoFromEvent:
828                                                 e];
829                }
830            }
831          else if (xEvent.xclient.message_type == dnd.XdndStatus)
832            {
833              Window target;
834              Atom action;
835              NSDragOperation operation;
836
837              NSDebugLLog(@"NSDragging", @"  XdndStatus message\n");
838              target = XDND_STATUS_TARGET_WIN(&xEvent);
839              eventLocation = NSMakePoint(0, 0);
840              if (XDND_STATUS_WILL_ACCEPT (&xEvent))
841                {
842                  action = XDND_STATUS_ACTION(&xEvent);
843                }
844              else
845                {
846                  action = NSDragOperationNone;
847                }
848
849              operation = GSDragOperationForAction(action);
850              e = [NSEvent otherEventWithType: NSAppKitDefined
851                           location: eventLocation
852                           modifierFlags: 0
853                           timestamp: 0
854                           windowNumber: cWin->number
855                           context: gcontext
856                           subtype: GSAppKitDraggingStatus
857                           data1: target
858                           data2: operation];
859            }
860          else if (xEvent.xclient.message_type == dnd.XdndLeave)
861            {
862              Window source;
863
864              NSDebugLLog(@"NSDragging", @"  XdndLeave message\n");
865              source = XDND_LEAVE_SOURCE_WIN(&xEvent);
866              eventLocation = NSMakePoint(0, 0);
867              e = [NSEvent otherEventWithType: NSAppKitDefined
868                           location: eventLocation
869                           modifierFlags: 0
870                           timestamp: 0
871                           windowNumber: cWin->number
872                           context: gcontext
873                           subtype: GSAppKitDraggingExit
874                           data1: 0
875                           data2: 0];
876              /* If this is a non-local drag, reset the dragInfo */
877              if ([XGServer _windowForXWindow: source] == NULL)
878                {
879                  [[XGDragView sharedDragView] resetDragInfo];
880                }
881            }
882          else if (xEvent.xclient.message_type == dnd.XdndDrop)
883            {
884              Window source;
885
886              NSDebugLLog(@"NSDragging", @"  XdndDrop message\n");
887              source = XDND_DROP_SOURCE_WIN(&xEvent);
888              eventLocation = NSMakePoint(0, 0);
889              time = XDND_DROP_TIME(&xEvent);
890              e = [NSEvent otherEventWithType: NSAppKitDefined
891                           location: eventLocation
892                           modifierFlags: 0
893                           timestamp: time / 1000.0
894                           windowNumber: cWin->number
895                           context: gcontext
896                           subtype: GSAppKitDraggingDrop
897                           data1: source
898                           data2: 0];
899            }
900          else if (xEvent.xclient.message_type == dnd.XdndFinished)
901            {
902              Window target;
903
904              NSDebugLLog(@"NSDragging", @"  XdndFinished message\n");
905              target = XDND_FINISHED_TARGET_WIN(&xEvent);
906              eventLocation = NSMakePoint(0, 0);
907              e = [NSEvent otherEventWithType: NSAppKitDefined
908                           location: eventLocation
909                           modifierFlags: 0
910                           timestamp: 0
911                           windowNumber: cWin->number
912                           context: gcontext
913                           subtype: GSAppKitDraggingFinished
914                           data1: target
915                           data2: 0];
916            }
917        }
918        break;
919
920      case ColormapNotify:
921        // colormap attribute
922        NSDebugLLog(@"NSEvent", @"%lu ColormapNotify\n",
923                    xEvent.xcolormap.window);
924        break;
925
926            // the window has been resized, change the width and height
927            // and update the window so the changes get displayed
928      case ConfigureNotify:
929        NSDebugLLog(@"NSEvent", @"%lu ConfigureNotify "
930                    @"x:%d y:%d w:%d h:%d b:%d %c", xEvent.xconfigure.window,
931                    xEvent.xconfigure.x, xEvent.xconfigure.y,
932                    xEvent.xconfigure.width, xEvent.xconfigure.height,
933                    xEvent.xconfigure.border_width,
934                    xEvent.xconfigure.send_event ? 'T' : 'F');
935        if (cWin == 0 || xEvent.xconfigure.window != cWin->ident)
936          {
937            generic.cachedWindow
938              = [XGServer _windowForXWindow:xEvent.xconfigure.window];
939          }
940
941        if (cWin != 0)
942          {
943            NSRect r, x, n, h;
944            NSTimeInterval ts = (NSTimeInterval)generic.lastMotion;
945
946            r = cWin->xframe;
947
948            x = NSMakeRect(xEvent.xconfigure.x,
949                           xEvent.xconfigure.y,
950                           xEvent.xconfigure.width,
951                           xEvent.xconfigure.height);
952
953            /*
954            According to the ICCCM, coordinates in synthetic events
955            (ie. non-zero send_event) are in root space, while coordinates
956            in real events are in the parent window's space. The parent
957            window might be some window manager window, so we can't
958            directly use those coordinates.
959
960            Thus, if the event is real, we use XTranslateCoordinates to
961            get the root space coordinates.
962            */
963            if (xEvent.xconfigure.send_event == 0)
964              {
965                int root_x, root_y;
966                Window root_child;
967                XTranslateCoordinates(dpy, xEvent.xconfigure.window,
968                                      RootWindow(dpy, cWin->screen_id),
969                                      0, 0,
970                                      &root_x, &root_y,
971                                      &root_child);
972                x.origin.x = root_x;
973                x.origin.y = root_y;
974              }
975
976            cWin->xframe = x;
977            n = [self _XFrameToOSFrame: x for: cWin];
978            NSDebugLLog(@"Moving",
979                        @"Update win %lu:\n   original:%@\n   new:%@",
980                        cWin->number, NSStringFromRect(r),
981                        NSStringFromRect(x));
982            /*
983             * Set size hints info to be up to date with new size.
984             */
985            h = [self _XFrameToXHints: x for: cWin];
986            cWin->siz_hints.width = h.size.width;
987            cWin->siz_hints.height = h.size.height;
988
989            /*
990            We only update the position hints if we're on the screen.
991            Otherwise, the window manager might not have added decorations
992            (if any) to the window yet. Since we compensate for decorations
993            when we set the position, this will confuse us and we'll end
994            up compensating twice, which makes windows drift.
995            */
996            if (cWin->map_state == IsViewable)
997              {
998                cWin->siz_hints.x = h.origin.x;
999                cWin->siz_hints.y = h.origin.y;
1000              }
1001
1002            /*
1003             * create GNUstep event(s)
1004             */
1005            if (!NSEqualSizes(r.size, x.size))
1006              {
1007		NSEvent *r;
1008
1009                /* Resize events move the origin. There's no good
1010                   place to pass this info back, so we put it in
1011                   the event location field */
1012                r = [NSEvent otherEventWithType: NSAppKitDefined
1013                             location: n.origin
1014                             modifierFlags: eventFlags
1015                             timestamp: ts / 1000.0
1016                             windowNumber: cWin->number
1017                             context: gcontext
1018                             subtype: GSAppKitWindowResized
1019                             data1: n.size.width
1020                             data2: n.size.height];
1021
1022		/* We don't add this event in event_queue, to don't delay
1023		 * its sent. Instead, send it directly to the window. If not,
1024		 * the programa can move/resize the window while we send
1025		 * this event, causing a confusion.
1026		 */
1027		[[NSApp windowWithWindowNumber: cWin->number] sendEvent: r];
1028              }
1029            if (!NSEqualPoints(r.origin, x.origin))
1030              {
1031		NSEvent *r;
1032                NSWindow *window;
1033
1034                r = [NSEvent otherEventWithType: NSAppKitDefined
1035                             location: eventLocation
1036                             modifierFlags: eventFlags
1037                             timestamp: ts / 1000.0
1038                             windowNumber: cWin->number
1039                             context: gcontext
1040                             subtype: GSAppKitWindowMoved
1041                             data1: n.origin.x
1042                             data2: n.origin.y];
1043
1044		/* We don't add this event in event_queue, to don't delay
1045		 * its sent. Instead, send it directly to the window. If not,
1046		 * the programa can move/resize the window while we send
1047		 * this event, causing a confusion.
1048		 */
1049                window = [NSApp windowWithWindowNumber: cWin->number];
1050                [window sendEvent: r];
1051                /* Update monitor_id of the backend window.
1052                   NSWindow may change screen pointer while processing
1053                   the event. */
1054                cWin->monitor_id = [[window screen] screenNumber];
1055              }
1056          }
1057        break;
1058
1059            // same as ConfigureNotify but we get this event
1060            // before the change has actually occurred
1061      case ConfigureRequest:
1062        NSDebugLLog(@"NSEvent", @"%lu ConfigureRequest\n",
1063                    xEvent.xconfigurerequest.window);
1064        break;
1065
1066            // a window has been created
1067      case CreateNotify:
1068        NSDebugLLog(@"NSEvent", @"%lu CreateNotify\n",
1069                    xEvent.xcreatewindow.window);
1070        break;
1071
1072            // a window has been destroyed
1073      case DestroyNotify:
1074        NSDebugLLog(@"NSEvent", @"%lu DestroyNotify\n",
1075                    xEvent.xdestroywindow.window);
1076        break;
1077
1078            // when the pointer enters a window
1079      case EnterNotify:
1080        NSDebugLLog(@"NSEvent", @"%lu EnterNotify\n",
1081                    xEvent.xcrossing.window);
1082        break;
1083
1084            // when the pointer leaves a window
1085      case LeaveNotify:
1086        NSDebugLLog(@"NSEvent", @"%lu LeaveNotify\n",
1087                    xEvent.xcrossing.window);
1088        if (cWin == 0 || xEvent.xcrossing.window != cWin->ident)
1089          {
1090            generic.cachedWindow
1091              = [XGServer _windowForXWindow: xEvent.xcrossing.window];
1092          }
1093        if (cWin == 0)
1094          break;
1095        eventLocation = NSMakePoint(-1,-1);
1096        e = [NSEvent otherEventWithType: NSAppKitDefined
1097                     location: eventLocation
1098                     modifierFlags: 0
1099                     timestamp: 0
1100                     windowNumber: cWin->number
1101                     context: gcontext
1102                     subtype: GSAppKitWindowLeave
1103                     data1: 0
1104                     data2: 0];
1105        break;
1106
1107            // the visibility of a window has changed
1108      case VisibilityNotify:
1109        NSDebugLLog(@"NSEvent", @"%lu VisibilityNotify %d\n",
1110                    xEvent.xvisibility.window, xEvent.xvisibility.state);
1111        if (cWin == 0 || xEvent.xvisibility.window != cWin->ident)
1112          {
1113            generic.cachedWindow
1114              = [XGServer _windowForXWindow:xEvent.xvisibility.window];
1115          }
1116        // sub-window ?
1117	/*
1118          {
1119            Window xw;
1120            xw = xEvent.xvisibility.window;
1121            while (cWin == 0)
1122              {
1123                Window rw, *cw; unsigned int nc;
1124                if ( !XQueryTree(dpy, xw, &rw, &xw, &cw, &nc) )
1125                  continue;
1126                if ( cw != NULL )
1127                  XFree(cw);
1128                generic.cachedWindow
1129                  = [XGServer _windowForXWindow:xw];
1130              }
1131          }
1132	*/
1133        if (cWin != 0)
1134          cWin->visibility = xEvent.xvisibility.state;
1135        break;
1136
1137        // a portion of the window has become visible and
1138        // we must redisplay it
1139      case Expose:
1140        NSDebugLLog(@"NSEvent", @"%lu Expose\n",
1141                    xEvent.xexpose.window);
1142        {
1143          if (cWin == 0 || xEvent.xexpose.window != cWin->ident)
1144            {
1145              generic.cachedWindow
1146                = [XGServer _windowForXWindow:xEvent.xexpose.window];
1147            }
1148          // sub-window ?
1149	  /*
1150          BOOL isSubWindow = NO;
1151
1152            {
1153              Window xw;
1154              xw = xEvent.xexpose.window;
1155              XWindowAttributes wa;
1156              // We should not found more than one level, but who knows ?
1157              while (cWin == 0)
1158                {
1159                  Window rw, *cw; unsigned int nc;
1160                  XGetWindowAttributes(dpy,xEvent.xexpose.window,&wa);
1161                  xEvent.xexpose.x += wa.x;
1162                  xEvent.xexpose.y += wa.y;
1163                  if ( !XQueryTree(dpy, xw, &rw, &xw, &cw, &nc) )
1164                    continue;
1165                  if ( cw != NULL )
1166                    XFree(cw);
1167                  generic.cachedWindow
1168                    = [XGServer _windowForXWindow:xw];
1169                }
1170              if ( xw != xEvent.xexpose.window )
1171                {
1172                  isSubWindow = YES;
1173                }
1174            }
1175	  */
1176          if (cWin != 0)
1177            {
1178              XRectangle rectangle;
1179
1180              rectangle.x = xEvent.xexpose.x;
1181              rectangle.y = xEvent.xexpose.y;
1182              rectangle.width = xEvent.xexpose.width;
1183              rectangle.height = xEvent.xexpose.height;
1184
1185              NSDebugLLog(@"NSEvent", @"Expose frame %d %d %d %d\n",
1186                          rectangle.x, rectangle.y,
1187                          rectangle.width, rectangle.height);
1188#if 0
1189              // ignore backing if sub-window
1190              [self _addExposedRectangle: rectangle : cWin->number : isSubWindow];
1191
1192              if (xEvent.xexpose.count == 0)
1193                [self _processExposedRectangles: cWin->number];
1194#else
1195              {
1196                NSRect rect;
1197                NSTimeInterval ts = (NSTimeInterval)generic.lastMotion;
1198
1199                rect = [self _XWinRectToOSWinRect: NSMakeRect(
1200                        rectangle.x, rectangle.y, rectangle.width, rectangle.height)
1201                             for: cWin];
1202                e = [NSEvent otherEventWithType: NSAppKitDefined
1203                             location: rect.origin
1204                             modifierFlags: eventFlags
1205                             timestamp: ts / 1000.0
1206                             windowNumber: cWin->number
1207                             context: gcontext
1208                             subtype: GSAppKitRegionExposed
1209                             data1: rect.size.width
1210                             data2: rect.size.height];
1211              }
1212
1213#endif
1214            }
1215          break;
1216        }
1217
1218      // keyboard focus entered a window
1219      case FocusIn:
1220        NSDebugLLog(@"NSEvent", @"%lu FocusIn\n",
1221                    xEvent.xfocus.window);
1222        if (cWin == 0 || xEvent.xfocus.window != cWin->ident)
1223          {
1224            generic.cachedWindow
1225                = [XGServer _windowForXWindow:xEvent.xfocus.window];
1226          }
1227        if (cWin == 0)
1228          break;
1229        NSDebugLLog(@"Focus", @"%lu got focus on %lu\n",
1230                    xEvent.xfocus.window, cWin->number);
1231        // Store this for debugging, may not be the real focus window
1232        generic.currentFocusWindow = cWin->number;
1233        if (xEvent.xfocus.serial == generic.focusRequestNumber)
1234          {
1235            /*
1236             * This is a response to our own request - so we mark the
1237             * request as complete.
1238             */
1239            generic.desiredFocusWindow = 0;
1240            generic.focusRequestNumber = 0;
1241          }
1242        break;
1243
1244            // keyboard focus left a window
1245      case FocusOut:
1246        {
1247          Window fw;
1248          int rev;
1249
1250          /*
1251           * See where the focus has moved to -
1252           * If it has gone to 'none' or 'PointerRoot' then
1253           * it's not one of ours.
1254           * If it has gone to our root window - use the icon window.
1255           * If it has gone to a window - we see if it is one of ours.
1256           */
1257          XGetInputFocus(xEvent.xfocus.display, &fw, &rev);
1258          NSDebugLLog(@"NSEvent", @"%lu FocusOut\n",
1259                      xEvent.xfocus.window);
1260          if (fw != None && fw != PointerRoot)
1261            {
1262              generic.cachedWindow = [XGServer _windowForXWindow: fw];
1263              if (cWin == 0)
1264                {
1265                  generic.cachedWindow = [XGServer _windowForXParent: fw];
1266                }
1267              if (cWin == 0)
1268                {
1269                  nswin = nil;
1270                }
1271              else
1272                {
1273                  nswin = GSWindowWithNumber(cWin->number);
1274                }
1275            }
1276          else
1277            {
1278              nswin = nil;
1279            }
1280          NSDebugLLog(@"Focus", @"Focus went to %lu (xwin %lu)\n",
1281                      (nswin != nil) ? cWin->number : 0, fw);
1282
1283          // Focus went to a window not in this application.
1284          if (nswin == nil)
1285            {
1286              [NSApp deactivate];
1287            }
1288
1289          // Clean up old focus request
1290          generic.cachedWindow
1291              = [XGServer _windowForXWindow: xEvent.xfocus.window];
1292          NSDebugLLog(@"Focus", @"%lu lost focus on %lu\n",
1293                      xEvent.xfocus.window, (cWin) ? cWin->number : 0);
1294          generic.currentFocusWindow = 0;
1295          if (cWin && generic.desiredFocusWindow == cWin->number)
1296            {
1297              /* Request not valid anymore since we lost focus */
1298              generic.focusRequestNumber = 0;
1299            }
1300        }
1301        break;
1302
1303      case GraphicsExpose:
1304        NSDebugLLog(@"NSEvent", @"%lu GraphicsExpose\n",
1305                    xEvent.xexpose.window);
1306        break;
1307
1308      case NoExpose:
1309        NSDebugLLog(@"NSEvent", @"NoExpose\n");
1310        break;
1311
1312      // window is moved because of a change in the size of its parent
1313      case GravityNotify:
1314        NSDebugLLog(@"NSEvent", @"%lu GravityNotify\n",
1315                    xEvent.xgravity.window);
1316        break;
1317
1318        // a key has been pressed
1319      case KeyPress:
1320        NSDebugLLog(@"NSEvent", @"%lu KeyPress\n",
1321                    xEvent.xkey.window);
1322        [self setLastTime: xEvent.xkey.time];
1323        e = process_key_event (&xEvent, self, NSKeyDown, event_queue, NO);
1324        break;
1325
1326        // a key has been released
1327      case KeyRelease:
1328        NSDebugLLog(@"NSEvent", @"%lu KeyRelease\n",
1329                    xEvent.xkey.window);
1330        [self setLastTime: xEvent.xkey.time];
1331
1332        /*
1333          For key repeats X creates two corresponding KeyRelease/KeyPress events.
1334          So, first we check for the KeyRelease event, take a look at the next
1335          event in the queue and look if they are a matching KeyRelease/KeyPress
1336          pair. If so, we ignore the current KeyRelease event.
1337        */
1338        if (XEventsQueued(dpy, QueuedAfterReading))
1339          {
1340            XEvent nev;
1341            XPeekEvent(dpy, &nev);
1342
1343            if (nev.type == KeyPress &&
1344                nev.xkey.window == xEvent.xkey.window &&
1345                nev.xkey.time == xEvent.xkey.time &&
1346                nev.xkey.keycode == xEvent.xkey.keycode)
1347              {
1348                // Ignore the current KeyRelease event.
1349              }
1350            else
1351              {
1352                /*
1353                if (nev.type == ClientMessage)
1354                  {
1355                    NSDebugLLog(@"NSEvent", @"Next event ClientMessage type %ld %s",
1356                                xEvent.xclient.message_type,
1357                                XGetAtomName(dpy, xEvent.xclient.message_type));
1358                  }
1359                */
1360                e = process_key_event(&xEvent, self, NSKeyUp, event_queue, NO);
1361              }
1362          }
1363        else
1364          {
1365            e = process_key_event(&xEvent, self, NSKeyUp, event_queue, NO);
1366          }
1367        break;
1368
1369        // reports the state of the keyboard when pointer or
1370        // focus enters a window
1371      case KeymapNotify:
1372        {
1373          if (_is_keyboard_initialized == NO)
1374            initialize_keyboard ();
1375
1376          NSDebugLLog(@"NSEvent", @"%lu KeymapNotify\n",
1377                      xEvent.xkeymap.window);
1378
1379          // Check if shift is pressed
1380          _shift_pressed = 0;
1381          if (check_modifier (&xEvent, XK_Shift_L))
1382            {
1383              _shift_pressed |= 1;
1384            }
1385          if (check_modifier (&xEvent, XK_Shift_R))
1386            {
1387              _shift_pressed |= 2;
1388            }
1389
1390          // Check if control is pressed
1391          _control_pressed = 0;
1392          if ((_control_keysyms[0] != NoSymbol)
1393              && check_modifier (&xEvent, _control_keysyms[0]))
1394            {
1395              _control_pressed |= 1;
1396            }
1397          if ((_control_keysyms[1] != NoSymbol)
1398              && check_modifier (&xEvent, _control_keysyms[1]))
1399            {
1400              _control_pressed |= 2;
1401            }
1402
1403          // Check if command is pressed
1404          _command_pressed = 0;
1405          if ((_command_keysyms[0] != NoSymbol)
1406              && check_modifier (&xEvent, _command_keysyms[0]))
1407            {
1408              _command_pressed |= 1;
1409            }
1410          if ((_command_keysyms[1] != NoSymbol)
1411              && check_modifier (&xEvent, _command_keysyms[1]))
1412            {
1413              _command_pressed |= 2;
1414            }
1415
1416          // Check if alt is pressed
1417          _alt_pressed = 0;
1418          if ((_alt_keysyms[0] != NoSymbol)
1419              && check_modifier (&xEvent, _alt_keysyms[0]))
1420            {
1421              _alt_pressed |= 1;
1422            }
1423          if ((_alt_keysyms[1] != NoSymbol)
1424              && check_modifier (&xEvent, _alt_keysyms[1]))
1425            {
1426              _alt_pressed |= 2;
1427            }
1428
1429          // Check if help is pressed
1430          _help_pressed = 0;
1431          if ((_help_keysyms[0] != NoSymbol)
1432              && check_modifier (&xEvent, _help_keysyms[0]))
1433            {
1434              _help_pressed |= 1;
1435            }
1436          if ((_help_keysyms[1] != NoSymbol)
1437              && check_modifier (&xEvent, _help_keysyms[1]))
1438            {
1439              _help_pressed |= 2;
1440            }
1441        }
1442        break;
1443
1444            // when a window changes state from ummapped to
1445            // mapped or vice versa
1446      case MapNotify:
1447        NSDebugLLog(@"NSEvent", @"%lu MapNotify\n",
1448                    xEvent.xmap.window);
1449        if (cWin == 0 || xEvent.xmap.window != cWin->ident)
1450          {
1451            generic.cachedWindow
1452                = [XGServer _windowForXWindow:xEvent.xmap.window];
1453          }
1454        if (cWin != 0)
1455          {
1456            cWin->map_state = IsViewable;
1457            /*
1458             * if the window that was just mapped wants the input
1459             * focus, re-do the request.
1460             */
1461            if (generic.desiredFocusWindow == cWin->number
1462                && generic.focusRequestNumber == 0)
1463              {
1464                NSDebugLLog(@"Focus", @"Refocusing %lu on map notify",
1465                            cWin->number);
1466                [self setinputfocus: cWin->number];
1467              }
1468            /*
1469             * Make sure that the newly mapped window displays.
1470             */
1471            nswin = GSWindowWithNumber(cWin->number);
1472            [nswin update];
1473          }
1474        break;
1475
1476            // Window is no longer visible.
1477      case UnmapNotify:
1478        NSDebugLLog(@"NSEvent", @"%lu UnmapNotify\n",
1479                    xEvent.xunmap.window);
1480        if (cWin == 0 || xEvent.xunmap.window != cWin->ident)
1481          {
1482            generic.cachedWindow
1483              = [XGServer _windowForXWindow:xEvent.xunmap.window];
1484          }
1485        if (cWin != 0)
1486          {
1487            cWin->map_state = IsUnmapped;
1488            cWin->visibility = -1;
1489          }
1490        break;
1491
1492            // like MapNotify but occurs before the request is carried out
1493      case MapRequest:
1494        NSDebugLLog(@"NSEvent", @"%lu MapRequest\n",
1495                    xEvent.xmaprequest.window);
1496        break;
1497
1498            // keyboard or mouse mapping has been changed by another client
1499      case MappingNotify:
1500        NSDebugLLog(@"NSEvent", @"%lu MappingNotify\n",
1501                    xEvent.xmapping.window);
1502        if ((xEvent.xmapping.request == MappingModifier)
1503            || (xEvent.xmapping.request == MappingKeyboard))
1504          {
1505            XRefreshKeyboardMapping (&xEvent.xmapping);
1506            set_up_num_lock ();
1507          }
1508        break;
1509
1510      case MotionNotify:
1511        NSDebugLLog(@"NSMotionEvent", @"%lu MotionNotify - %d %d\n",
1512                    xEvent.xmotion.window, xEvent.xmotion.x, xEvent.xmotion.y);
1513        {
1514          unsigned int        state;
1515
1516          /*
1517           * Compress motion events to avoid flooding.
1518           */
1519          while (XPending(xEvent.xmotion.display))
1520            {
1521              XEvent        peek;
1522
1523              XPeekEvent(xEvent.xmotion.display, &peek);
1524              if (peek.type == MotionNotify
1525                  && xEvent.xmotion.window == peek.xmotion.window
1526                  && xEvent.xmotion.subwindow == peek.xmotion.subwindow)
1527                {
1528                  XNextEvent(xEvent.xmotion.display, &xEvent);
1529                }
1530              else
1531                {
1532                  break;
1533                }
1534            }
1535
1536          generic.lastMotion = xEvent.xmotion.time;
1537          [self setLastTime: generic.lastMotion];
1538          state = xEvent.xmotion.state;
1539          if (state & generic.lMouseMask)
1540            {
1541              eventType = NSLeftMouseDragged;
1542            }
1543          else if (state & generic.rMouseMask)
1544            {
1545              eventType = NSRightMouseDragged;
1546            }
1547          else if (state & generic.mMouseMask)
1548            {
1549              eventType = NSOtherMouseDragged;
1550            }
1551          else
1552            {
1553              eventType = NSMouseMoved;
1554            }
1555
1556          eventFlags = process_modifier_flags(state);
1557          // if pointer is grabbed use grab window instead
1558          xWin = (grabWindow == 0)
1559            ? xEvent.xmotion.window : grabWindow;
1560          if (cWin == 0 || xWin != cWin->ident)
1561            generic.cachedWindow = [XGServer _windowForXWindow: xWin];
1562          if (cWin == 0)
1563            break;
1564
1565          deltaX = - eventLocation.x;
1566          deltaY = - eventLocation.y;
1567          eventLocation = NSMakePoint(xEvent.xmotion.x, xEvent.xmotion.y);
1568          eventLocation = [self _XPointToOSPoint: eventLocation
1569                                             for: cWin];
1570          deltaX += eventLocation.x;
1571          deltaY += eventLocation.y;
1572
1573          e = [NSEvent mouseEventWithType: eventType
1574                       location: eventLocation
1575                       modifierFlags: eventFlags
1576                       timestamp: (NSTimeInterval)generic.lastTime / 1000.0
1577                       windowNumber: cWin->number
1578                       context: gcontext
1579                       eventNumber: xEvent.xbutton.serial
1580                       clickCount: clickCount
1581                       pressure: 1.0
1582                       buttonNumber: 0 /* FIXME */
1583                       deltaX: deltaX
1584                       deltaY: deltaY
1585                       deltaZ: 0];
1586          break;
1587        }
1588
1589      // a window property has changed or been deleted
1590      case PropertyNotify:
1591        NSDebugLLog(@"NSEvent", @"%lu PropertyNotify - '%s'\n",
1592                    xEvent.xproperty.window,
1593                    XGetAtomName(dpy, xEvent.xproperty.atom));
1594	if (xEvent.xproperty.atom == generic.WM_STATE_ATOM)
1595	  {
1596	    if (cWin == 0 || xEvent.xproperty.window != cWin->ident)
1597	      {
1598		generic.cachedWindow
1599		  = [XGServer _windowForXWindow: xEvent.xproperty.window];
1600	      }
1601	    if (cWin != 0)
1602	      {
1603		int new_state;
1604
1605		/* Get the new window state */
1606		if (xEvent.xproperty.state == PropertyNewValue)
1607		  new_state = [self _wm_state: xEvent.xproperty.window];
1608		else
1609		  new_state = WithdrawnState;
1610
1611		switch (new_state)
1612		  {
1613		  case IconicState:
1614		    /* Post miniaturize event upon transition from NormalState
1615		       to IconicState. If the window manager supports the ewmh
1616		       specification, also check that the _NET_WM_STATE
1617		       property includes _NET_WM_STATE_HIDDEN. */
1618		    /* Note: Don't rely on WM_STATE (nor on _NET_WM_STATE) with
1619		       Window Maker, since it is impossible to distinguish
1620		       miniaturized windows from hidden windows by their window
1621		       properties. Fortunately, Window Maker sends us a client
1622		       message when a window is miniaturized. */
1623		    if ((generic.wm & XGWM_WINDOWMAKER) == 0 &&
1624			cWin->wm_state == NormalState &&
1625			((generic.wm & XGWM_EWMH) == 0 ||
1626			 [self _ewmh_isHidden: xEvent.xproperty.window] == YES))
1627		      {
1628			/* Same event as when we get ClientMessage with the
1629			 * atom equal to generic._GNUSTEP_WM_MINIATURIZE_WINDOW_ATOM
1630			 */
1631			NSDebugLLog(@"Miniaturize", @"%lu miniaturized",
1632				    cWin->number);
1633			eventLocation = NSMakePoint(0,0);
1634			e = [NSEvent otherEventWithType: NSAppKitDefined
1635				     location: eventLocation
1636				     modifierFlags: 0
1637				     timestamp: xEvent.xproperty.time / 1000
1638				     windowNumber: cWin->number
1639				     context: gcontext
1640				     subtype: GSAppKitWindowMiniaturize
1641				     data1: 0
1642				     data2: 0];
1643		      }
1644		    break;
1645
1646		  case NormalState:
1647		    /* Post deminiaturize event upon transition from IconicState
1648		       to NormalState, but only if our window is actually
1649		       miniaturized. */
1650		    if (cWin->wm_state == IconicState &&
1651			[GSWindowWithNumber(cWin->number) isMiniaturized])
1652		      {
1653			NSDebugLLog(@"Miniaturize", @"%lu deminiaturized",
1654				    cWin->number);
1655			eventLocation = NSMakePoint(0,0);
1656			e = [NSEvent otherEventWithType: NSAppKitDefined
1657				     location: eventLocation
1658				     modifierFlags: 0
1659				     timestamp: xEvent.xproperty.time / 1000
1660				     windowNumber: cWin->number
1661				     context: gcontext
1662				     subtype: GSAppKitWindowDeminiaturize
1663				     data1: 0
1664				     data2: 0];
1665		      }
1666		    break;
1667		  }
1668
1669		/* save the new state */
1670		cWin->wm_state = new_state;
1671	      }
1672	  }
1673        break;
1674
1675            // a client successfully reparents a window
1676      case ReparentNotify:
1677        NSDebugLLog(@"NSEvent", @"%lu ReparentNotify - offset %d %d\n",
1678                    xEvent.xreparent.window, xEvent.xreparent.x,
1679                    xEvent.xreparent.y);
1680        if (cWin == 0 || xEvent.xreparent.window != cWin->ident)
1681          {
1682            generic.cachedWindow
1683              = [XGServer _windowForXWindow:xEvent.xreparent.window];
1684          }
1685        if (cWin != 0)
1686          {
1687            cWin->parent = xEvent.xreparent.parent;
1688          }
1689
1690        if (cWin != 0 && xEvent.xreparent.parent != cWin->root
1691          && (xEvent.xreparent.x != 0 || xEvent.xreparent.y != 0))
1692          {
1693            Window parent = xEvent.xreparent.parent;
1694            XWindowAttributes wattr;
1695            float l;
1696            float r;
1697            float t;
1698            float b;
1699            Offsets *o;
1700
1701            /* Get the WM offset info which we hope is the same
1702             * for all parented windows with the same style.
1703             * The coordinates in the event are insufficient to determine
1704             * the offsets as the new parent window may have a border,
1705             * so we must get the attributes of that window and use them
1706             * to determine our offsets.
1707             */
1708            XGetWindowAttributes(dpy, parent, &wattr);
1709            NSDebugLLog(@"NSEvent", @"Parent border,width,height %d,%d,%d\n",
1710              wattr.border_width, wattr.width, wattr.height);
1711            l = xEvent.xreparent.x + wattr.border_width;
1712            t = xEvent.xreparent.y + wattr.border_width;
1713
1714            /* Find total parent size and subtract window size and
1715             * top-left-corner offset to determine bottom-right-corner
1716             * offset.
1717             */
1718            r = wattr.width + wattr.border_width * 2;
1719            r -= (cWin->xframe.size.width + l);
1720            b = wattr.height + wattr.border_width * 2;
1721            b -= (cWin->xframe.size.height + t);
1722
1723            // Some window manager e.g. KDE2 put in multiple windows,
1724            // so we have to find the right parent, closest to root
1725            /* FIXME: This section of code has caused problems with
1726               certain users. An X error occurs in XQueryTree and
1727               later a seg fault in XFree. It's 'commented' out for
1728               now unless you set the default 'GSDoubleParentWindows'
1729            */
1730            if (generic.flags.doubleParentWindow)
1731              {
1732                Window new_parent = parent;
1733
1734                r = wattr.width + wattr.border_width * 2;
1735                b = wattr.height + wattr.border_width * 2;
1736                while (new_parent && (new_parent != cWin->root))
1737                  {
1738                    Window root;
1739                    Window *children;
1740                    unsigned int nchildren;
1741
1742                    parent = new_parent;
1743                    NSLog(@"QueryTree window is %lu (cwin root %lu)",
1744                          parent, cWin->root);
1745                    if (!XQueryTree(dpy, parent, &root, &new_parent,
1746                      &children, &nchildren))
1747                      {
1748                        new_parent = None;
1749                        if (children)
1750                          {
1751                            NSLog(@"Bad pointer from failed X call?");
1752                            children = 0;
1753                          }
1754                      }
1755                    if (children)
1756                      {
1757                        XFree(children);
1758                      }
1759                    if (new_parent && new_parent != cWin->root)
1760                      {
1761                        XWindowAttributes pattr;
1762
1763                        XGetWindowAttributes(dpy, parent, &pattr);
1764                        l += pattr.x + pattr.border_width;
1765                        t += pattr.y + pattr.border_width;
1766                        r = pattr.width + pattr.border_width * 2;
1767                        b = pattr.height + pattr.border_width * 2;
1768                      }
1769                  } /* while */
1770                r -= (cWin->xframe.size.width + l);
1771                b -= (cWin->xframe.size.height + t);
1772              } /* generic.flags.doubleParentWindow */
1773
1774            o = generic.offsets + (cWin->win_attrs.window_style & 15);
1775            if (o->known == NO)
1776              {
1777                o->l = l;
1778                o->r = r;
1779                o->t = t;
1780                o->b = b;
1781                o->known = YES;
1782                /* FIXME: if offsets have changed, from previously guessed
1783                 * versions, we should go through window list and fix up
1784                 * hints.
1785                 */
1786              }
1787            else
1788              {
1789                BOOL changed = NO;
1790
1791                if (l != o->l)
1792                  {
1793                    NSLog(@"Ignore left offset change from %d to %d",
1794                      (int)o->l, (int)l);
1795                    changed = YES;
1796                  }
1797                if (r != o->r)
1798                  {
1799                    NSLog(@"Ignore right offset change from %d to %d",
1800                      (int)o->r, (int)r);
1801                    changed = YES;
1802                  }
1803                if (t != o->t)
1804                  {
1805                    NSLog(@"Ignore top offset change from %d to %d",
1806                      (int)o->t, (int)t);
1807                    changed = YES;
1808                  }
1809                if (b != o->b)
1810                  {
1811                    NSLog(@"Ignore bottom offset change from %d to %d",
1812                      (int)o->b, (int)b);
1813                    changed = YES;
1814                  }
1815                if (changed == YES)
1816                  {
1817                    NSLog(@"Reparent was with offset %d %d\n",
1818                      xEvent.xreparent.x, xEvent.xreparent.y);
1819                    NSLog(@"Parent border,width,height %d,%d,%d\n",
1820                      wattr.border_width, wattr.width, wattr.height);
1821                  }
1822              }
1823
1824            /* Work around a bug in Window Maker, which does not preserve
1825             * the document edited status and uses the wrong close button
1826             * when a window is shown again after hiding it
1827             */
1828            if (generic.wm & XGWM_WINDOWMAKER)
1829              {
1830/* Warning ... X-bug .. when we specify 32bit data X actually expects data
1831 * of type 'long' or 'unsigned long' even on machines where those types
1832 * hold 64bit values.
1833 */
1834                XChangeProperty(dpy, cWin->ident, generic._GNUSTEP_WM_ATTR_ATOM,
1835                                generic._GNUSTEP_WM_ATTR_ATOM, 32, PropModeReplace,
1836                                (unsigned char *)&cWin->win_attrs,
1837                                sizeof(GNUstepWMAttributes)/sizeof(CARD32));
1838              }
1839          }
1840        break;
1841
1842            // another client attempts to change the size of a window
1843      case ResizeRequest:
1844        NSDebugLLog(@"NSEvent", @"%lu ResizeRequest\n",
1845                    xEvent.xresizerequest.window);
1846        break;
1847
1848            // events dealing with the selection
1849      case SelectionClear:
1850        NSDebugLLog(@"NSEvent", @"%lu SelectionClear\n",
1851                    xEvent.xselectionclear.window);
1852        break;
1853
1854      case SelectionNotify:
1855        NSDebugLLog(@"NSEvent", @"%lu SelectionNotify\n",
1856                    xEvent.xselection.requestor);
1857        break;
1858
1859      case SelectionRequest:
1860        NSDebugLLog(@"NSEvent", @"%lu SelectionRequest\n",
1861                    xEvent.xselectionrequest.requestor);
1862        {
1863          NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSDragPboard];
1864          NSArray *types = [pb types];
1865          NSData *data = nil;
1866          Atom xType = xEvent.xselectionrequest.target;
1867
1868          if (((xType == generic.UTF8_STRING_ATOM) ||
1869               (xType == XA_STRING) ||
1870               (xType == generic.TEXT_ATOM)) &&
1871              [types containsObject: NSStringPboardType])
1872            {
1873              NSString *s = [pb stringForType: NSStringPboardType];
1874
1875              if (xType == generic.UTF8_STRING_ATOM)
1876                {
1877                  data = [s dataUsingEncoding: NSUTF8StringEncoding];
1878                }
1879              else if ((xType == XA_STRING) || (xType == generic.TEXT_ATOM))
1880                {
1881                  data = [s dataUsingEncoding: NSISOLatin1StringEncoding];
1882                }
1883            }
1884          // FIXME: Add support for more types. See: xpbs.m
1885
1886          if (data != nil)
1887            {
1888              DndClass dnd = xdnd();
1889
1890              // Send the data to the other process
1891              xdnd_selection_send(&dnd, &xEvent.xselectionrequest,
1892                                  (unsigned char *)[data bytes], [data length]);
1893            }
1894        }
1895        break;
1896
1897            // We shouldn't get here unless we forgot to trap an event above
1898      default:
1899#ifdef XSHM
1900        if (xEvent.type == XShmGetEventBase(dpy)+ShmCompletion
1901            && [gcontext respondsToSelector: @selector(gotShmCompletion:)])
1902          {
1903            [gcontext gotShmCompletion:
1904                        ((XShmCompletionEvent *)&xEvent)->drawable];
1905            break;
1906          }
1907#endif
1908#ifdef HAVE_XRANDR
1909        int randr_event_type = randrEventBase + RRScreenChangeNotify;
1910        if (xEvent.type == randr_event_type
1911            && (xEvent.xconfigure.window == RootWindow(dpy, defScreen)))
1912          {
1913            // Check if other RandR events are waiting in the queue.
1914            XSync(dpy, 0);
1915            while (XCheckTypedEvent(dpy, randr_event_type, &xEvent)) {;}
1916
1917            XRRUpdateConfiguration(event);
1918            // Regenerate NSScreens
1919            [NSScreen resetScreens];
1920            // Notify application about screen parameters change
1921            [[NSNotificationCenter defaultCenter]
1922                      postNotificationName: NSApplicationDidChangeScreenParametersNotification
1923                                    object: NSApp];
1924          }
1925        break;
1926#endif
1927        NSLog(@"Received an untrapped event\n");
1928        break;
1929    }
1930  if (e)
1931    {
1932      [event_queue addObject: e];
1933    }
1934  e = nil;
1935}
1936
1937/*
1938 * WM is asking us to take the keyboard focus
1939 */
1940- (NSEvent *)_handleTakeFocusAtom: (XEvent)xEvent
1941                       forContext: (NSGraphicsContext *)gcontext
1942{
1943  NSWindow *keyWindow = [NSApp keyWindow];
1944  int key_num = [keyWindow windowNumber];
1945  NSEvent *e = nil;
1946
1947  NSDebugLLog(@"Focus",
1948              @"TakeFocus received by: %li (%lu) (focused = %lu, key = %d)",
1949              cWin->number, xEvent.xfocus.window,
1950              generic.currentFocusWindow, key_num);
1951
1952  /* Invalidate the previous request. It's possible the app lost focus
1953     before this request was fufilled and we are being focused again,
1954     or ??? */
1955  {
1956    generic.focusRequestNumber = 0;
1957    generic.desiredFocusWindow = 0;
1958  }
1959
1960  /* Sometimes window managers lose the setinputfocus on the key window
1961   * e.g. when ordering out a window with focus then ordering in the key window.
1962   * it might search for a window until one accepts its take focus request.
1963   */
1964  if (key_num == 0)
1965    {
1966      cWin->ignore_take_focus = NO;
1967    }
1968  else if (cWin->number == [[[NSApp mainMenu] window] windowNumber])
1969    {
1970      cWin->ignore_take_focus = NO;
1971    }
1972
1973  /* We'd like to send this event directly to the front-end to handle,
1974     but the front-end polls events so slowly compared the speed at
1975     which X events could potentially come that we could easily get
1976     out of sync, particularly when there are a lot of window
1977     events */
1978  if ([NSApp isHidden])
1979    {
1980      /* This often occurs when hidding an app, since a bunch of
1981         windows get hidden at once, and the WM is searching for a
1982         window to take focus after each one gets hidden. */
1983      NSDebugLLog(@"Focus", @"WM take focus while hiding");
1984    }
1985  else if (cWin->ignore_take_focus == YES)
1986    {
1987      NSDebugLLog(@"Focus", @"Ignoring window focus request");
1988      cWin->ignore_take_focus = NO;
1989    }
1990  else if (cWin->number == key_num)
1991    {
1992      NSDebugLLog(@"Focus", @"Reasserting key window");
1993      [GSServerForWindow(keyWindow) setinputfocus: key_num];
1994    }
1995  else if (key_num
1996           && cWin->number == [[[NSApp mainMenu] window] windowNumber])
1997    {
1998      gswindow_device_t *key_window = [XGServer _windowWithTag:key_num];
1999      /* This might occur when the window manager just wants someone
2000         to become key, so it tells the main menu (typically the first
2001         menu in the list), but since we already have a window that
2002         was key before, use that instead */
2003      NSDebugLLog(@"Focus", @"Key window is already %d", key_num);
2004      if (key_window->map_state == IsUnmapped) {
2005        /* `key_window` was unmapped by window manager.
2006           this window and `key_window` are on the different workspace. */
2007        [GSServerForWindow(keyWindow) setinputfocus: cWin->number];
2008      }
2009      else {
2010        [GSServerForWindow(keyWindow) setinputfocus: key_num];
2011      }
2012    }
2013  else
2014    {
2015      NSPoint eventLocation;
2016      /*
2017       * Here the app asked for this (if keyWindow==nil) or there was a
2018       * click on the title bar or some other reason (window mapped,
2019       * etc). We don't necessarily want to forward the event for the
2020       * last reason but we just have to deal with that since we can
2021       * never be sure if it's necessary.
2022       */
2023      eventLocation = NSMakePoint(0,0);
2024      e = [NSEvent otherEventWithType:NSAppKitDefined
2025                   location: eventLocation
2026                   modifierFlags: 0
2027                   timestamp: 0
2028                   windowNumber: cWin->number
2029                   context: gcontext
2030                   subtype: GSAppKitWindowFocusIn
2031                   data1: 0
2032                   data2: 0];
2033    }
2034  return e;
2035}
2036
2037
2038// Return the key_sym corresponding to the user defaults string given,
2039// or fallback if no default is registered.
2040static KeySym
2041key_sym_from_defaults (Display *display, NSUserDefaults *defaults,
2042                       NSString *keyDefaultKey, KeySym fallback)
2043{
2044  NSString *keyDefaultName;
2045  KeySym key_sym;
2046
2047  keyDefaultName = [defaults stringForKey: keyDefaultKey];
2048  if (keyDefaultName == nil)
2049    return fallback;
2050
2051  key_sym = XStringToKeysym ([keyDefaultName cString]);
2052#if 0
2053  if (key_sym == NoSymbol && [keyDefaultName intValue] > 0)
2054    {
2055      key_sym = [keyDefaultName intValue];
2056    }
2057#endif
2058  if (key_sym == NoSymbol)
2059    {
2060      // This is not necessarily an error.
2061      // If you want on purpose to disable a key,
2062      // set its default to 'NoSymbol'.
2063      NSLog (@"KeySym %@ not found; disabling %@", keyDefaultName,
2064                                                   keyDefaultKey);
2065    }
2066
2067  return key_sym;
2068}
2069
2070// This function should be called before any keyboard event is dealed with.
2071static void
2072initialize_keyboard (void)
2073{
2074  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
2075  Display *display = [XGServer xDisplay];
2076
2077  // Below must be stored and checked as keysyms, not keycodes, since
2078  // more than one keycode may be mapped t the same keysym
2079  // Initialize Control
2080  _control_keysyms[0] = key_sym_from_defaults(display, defaults,
2081                                              @"GSFirstControlKey",
2082                                              XK_Control_L);
2083
2084  _control_keysyms[1] = key_sym_from_defaults(display, defaults,
2085                                              @"GSSecondControlKey",
2086                                              XK_Control_R);
2087
2088  if (_control_keysyms[0] == _control_keysyms[1])
2089    _control_keysyms[1] = NoSymbol;
2090
2091  // Initialize Command
2092  _command_keysyms[0] = key_sym_from_defaults(display, defaults,
2093                                              @"GSFirstCommandKey",
2094                                              XK_Alt_L);
2095
2096  _command_keysyms[1] = key_sym_from_defaults(display, defaults,
2097                                              @"GSSecondCommandKey",
2098                                              NoSymbol);
2099
2100  if (_command_keysyms[0] == _command_keysyms[1])
2101    _command_keysyms[1] = NoSymbol;
2102
2103  // Initialize Alt
2104  _alt_keysyms[0] = key_sym_from_defaults(display, defaults,
2105                                          @"GSFirstAlternateKey",
2106                                          XK_Alt_R);
2107  if (XKeysymToKeycode(display, _alt_keysyms[0]) == 0)
2108    _alt_keysyms[0] = XK_Mode_switch;
2109
2110  _alt_keysyms[1] = key_sym_from_defaults(display, defaults,
2111                                          @"GSSecondAlternateKey",
2112                                          NoSymbol);
2113
2114  if (_alt_keysyms[0] == _alt_keysyms[1])
2115    _alt_keysyms[1] = NoSymbol;
2116
2117  // Initialize Help
2118  _help_keysyms[0] = key_sym_from_defaults(display, defaults,
2119                                          @"GSFirstHelpKey",
2120                                          XK_Help);
2121  if (XKeysymToKeycode(display, _help_keysyms[0]) == 0)
2122    _help_keysyms[0] = NoSymbol;
2123
2124  _help_keysyms[1] = key_sym_from_defaults(display, defaults,
2125                                          @"GSSecondHelpKey",
2126                                          XK_Super_L);
2127
2128  if (_help_keysyms[0] == _help_keysyms[1])
2129    _help_keysyms[1] = NoSymbol;
2130
2131
2132  set_up_num_lock ();
2133  _mod_ignore_shift = ![defaults boolForKey: @"GSModifiersAreNotKeys"];
2134
2135  _is_keyboard_initialized = YES;
2136}
2137
2138
2139static void
2140set_up_num_lock (void)
2141{
2142  XModifierKeymap *modifier_map;
2143  int i, j;
2144  unsigned int modifier_masks[8] =
2145  {
2146    ShiftMask, LockMask, ControlMask, Mod1Mask,
2147    Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
2148  };
2149  Display *display = [XGServer xDisplay];
2150  KeyCode _num_lock_keycode;
2151
2152  // Get NumLock keycode
2153  _num_lock_keycode = XKeysymToKeycode (display, XK_Num_Lock);
2154  if (_num_lock_keycode == 0)
2155    {
2156      // Weird.  There is no NumLock in this keyboard.
2157      _num_lock_mask = 0;
2158      return;
2159    }
2160
2161  // Get the current modifier mapping
2162  modifier_map = XGetModifierMapping (display);
2163
2164  // Scan the modifiers for NumLock
2165  for (j = 0; j < 8; j++)
2166    for (i = 0; i < (modifier_map->max_keypermod); i++)
2167      {
2168        if ((modifier_map->modifiermap)[i + j*modifier_map->max_keypermod]
2169          == _num_lock_keycode)
2170          {
2171            _num_lock_mask = modifier_masks[j];
2172            XFreeModifiermap (modifier_map);
2173            return;
2174          }
2175      }
2176  // Weird.  NumLock is not among the modifiers
2177  _num_lock_mask = 0;
2178  XFreeModifiermap (modifier_map);
2179  return;
2180}
2181
2182static BOOL
2183keysym_is_X_modifier (KeySym keysym)
2184{
2185  switch (keysym)
2186    {
2187    case XK_Num_Lock:
2188    case XK_Caps_Lock:
2189    case XK_Shift_Lock:
2190      return YES;
2191
2192    default:
2193      return NO;
2194    }
2195}
2196
2197static NSEvent*
2198process_key_event (XEvent* xEvent, XGServer* context, NSEventType eventType,
2199                   NSMutableArray *event_queue, BOOL keyRepeat)
2200{
2201  NSString *keys, *ukeys;
2202  KeySym keysym;
2203  NSPoint eventLocation;
2204  unsigned short keyCode;
2205  unsigned int eventFlags;
2206  unichar unicode;
2207  NSEvent *event = nil;
2208  NSEventType originalType;
2209  gswindow_device_t *window;
2210  int shift_key = 0;
2211  int control_key = 0;
2212  int command_key = 0;
2213  int alt_key = 0;
2214  int help_key = 0;
2215  KeySym modKeysym;  // process modifier independently of shift, etc.
2216
2217  if (_is_keyboard_initialized == NO)
2218    initialize_keyboard ();
2219
2220  window = [XGServer _windowWithTag: [[NSApp keyWindow] windowNumber]];
2221  if (!window)
2222    {
2223      // No key event if we don't have a key window
2224      return nil;
2225    }
2226
2227  /* Process location */
2228  eventLocation.x = xEvent->xbutton.x;
2229  eventLocation.y = xEvent->xbutton.y;
2230  eventLocation = [context _XPointToOSPoint: eventLocation
2231                                        for: window];
2232
2233  /* Process characters */
2234  keys = [context->inputServer lookupStringForEvent: (XKeyEvent *)xEvent
2235                 window: window
2236                 keysym: &keysym];
2237
2238  /* Process keycode */
2239  keyCode = ((XKeyEvent *)xEvent)->keycode;
2240  //ximKeyCode = XKeysymToKeycode([XGServer currentXDisplay],keysym);
2241
2242  /* Process NSFlagsChanged events.  We can't use a switch because we
2243     are not comparing to constants. Make sure keySym is not NoSymbol since
2244     XIM events can potentially return this. */
2245  /* Possibly ignore shift/other modifier state in determining KeySym to
2246     work around correct but undesired behavior with shifted modifiers.
2247     See Back defaults documentation for "GSModifiersAreNotKeys". */
2248  modKeysym = (_mod_ignore_shift == YES) ?
2249      XLookupKeysym((XKeyEvent *)xEvent, 0) : keysym;
2250  if (modKeysym != NoSymbol)
2251    {
2252      if (modKeysym == XK_Shift_L)
2253        {
2254          shift_key = 1;
2255        }
2256      else if (modKeysym == XK_Shift_R)
2257        {
2258          shift_key = 2;
2259        }
2260      else if (modKeysym == _control_keysyms[0])
2261        {
2262          control_key = 1;
2263        }
2264      else if (modKeysym == _control_keysyms[1])
2265        {
2266          control_key = 2;
2267        }
2268      else if (modKeysym == _command_keysyms[0])
2269        {
2270          command_key = 1;
2271        }
2272      else if (modKeysym == _command_keysyms[1])
2273        {
2274          command_key = 2;
2275        }
2276      else if (modKeysym == _alt_keysyms[0])
2277        {
2278          alt_key = 1;
2279        }
2280      else if (modKeysym == _alt_keysyms[1])
2281        {
2282          alt_key = 2;
2283        }
2284      else if (modKeysym == _help_keysyms[0])
2285        {
2286          help_key = 1;
2287        }
2288      else if (modKeysym == _help_keysyms[1])
2289        {
2290          help_key = 2;
2291        }
2292    }
2293
2294  originalType = eventType;
2295  if (shift_key || control_key || command_key || alt_key || help_key)
2296    {
2297      eventType = NSFlagsChanged;
2298      if (xEvent->xkey.type == KeyPress)
2299        {
2300          if (shift_key)
2301            _shift_pressed |= shift_key;
2302          if (control_key)
2303            _control_pressed |= control_key;
2304          if (command_key)
2305            _command_pressed |= command_key;
2306          if (alt_key)
2307            _alt_pressed |= alt_key;
2308          if (help_key)
2309            _help_pressed |= help_key;
2310        }
2311      else if (xEvent->xkey.type == KeyRelease)
2312        {
2313          if (shift_key)
2314            _shift_pressed &= ~shift_key;
2315          if (control_key)
2316            _control_pressed &= ~control_key;
2317          if (command_key)
2318            _command_pressed &= ~command_key;
2319          if (alt_key)
2320            _alt_pressed &= ~alt_key;
2321          if (help_key)
2322            _help_pressed &= ~help_key;
2323        }
2324    }
2325
2326  /* Process modifiers */
2327  eventFlags = process_modifier_flags (xEvent->xkey.state);
2328
2329  /* Add NSNumericPadKeyMask if the key is in the KeyPad */
2330  if (IsKeypadKey (keysym))
2331    eventFlags = eventFlags | NSNumericPadKeyMask;
2332
2333  NSDebugLLog (@"NSKeyEvent", @"keysym=%lu, keyCode=%d flags=%d (state=%d)",
2334              keysym, keyCode, eventFlags, ((XKeyEvent *)xEvent)->state);
2335
2336  /* Add NSFunctionKeyMask if the key is a function or a misc function key */
2337  /* We prefer not to do this and do it manually in process_char
2338     because X's idea of what is a function key seems to be different
2339     from OPENSTEP's one */
2340  /* if (IsFunctionKey (keysym) || IsMiscFunctionKey (keysym))
2341       eventFlags = eventFlags | NSFunctionKeyMask; */
2342
2343  /* First, check to see if the key event if a Shift, NumLock or
2344     CapsLock or ShiftLock keypress/keyrelease.  If it is, then use a
2345     NSFlagsChanged event type.  This will generate a NSFlagsChanged
2346     event each time you press/release a shift key, even if the flags
2347     haven't actually changed.  I don't see this as a problem - if we
2348     didn't, the shift keypress/keyrelease event would never be
2349     notified to the application.
2350
2351     NB - to know if shift was pressed, we need to check the X keysym
2352     - it doesn't work to compare the X modifier flags of this
2353     keypress X event with the ones of the previous one, because when
2354     you press Shift, the X shift keypress event has the *same* X
2355     modifiers flags as the X keypress event before it - only
2356     keypresses coming *after* the shift keypress will get a different
2357     X modifier mask.  */
2358  if (keysym_is_X_modifier (keysym))
2359    {
2360      eventType = NSFlagsChanged;
2361    }
2362
2363
2364  if (help_key)
2365    {
2366      unicode = NSHelpFunctionKey;
2367      keys = [NSString stringWithCharacters: &unicode  length: 1];
2368      if (originalType == NSKeyDown)
2369        {
2370          event = [NSEvent keyEventWithType: NSKeyDown
2371                   location: eventLocation
2372                   modifierFlags: eventFlags
2373                   timestamp: (NSTimeInterval)xEvent->xkey.time / 1000.0
2374                   windowNumber: window->number
2375                   context: GSCurrentContext()
2376                   characters: keys
2377                   charactersIgnoringModifiers: keys
2378                   isARepeat: keyRepeat
2379                   keyCode: keyCode];
2380          [event_queue addObject: event];
2381          event = [NSEvent keyEventWithType: NSFlagsChanged
2382                   location: eventLocation
2383                   modifierFlags: eventFlags
2384                   timestamp: (NSTimeInterval)xEvent->xkey.time / 1000.0
2385                   windowNumber: window->number
2386                   context: GSCurrentContext()
2387                   characters: keys
2388                   charactersIgnoringModifiers: keys
2389                   isARepeat: NO
2390                   keyCode: keyCode];
2391          return event;
2392        }
2393      else
2394        {
2395          event = [NSEvent keyEventWithType: NSFlagsChanged
2396                   location: eventLocation
2397                   modifierFlags: eventFlags
2398                   timestamp: (NSTimeInterval)xEvent->xkey.time / 1000.0
2399                   windowNumber: window->number
2400                   context: GSCurrentContext()
2401                   characters: keys
2402                   charactersIgnoringModifiers: keys
2403                   isARepeat: NO
2404                   keyCode: keyCode];
2405          [event_queue addObject: event];
2406          event = [NSEvent keyEventWithType: NSKeyUp
2407                   location: eventLocation
2408                   modifierFlags: eventFlags
2409                   timestamp: (NSTimeInterval)xEvent->xkey.time / 1000.0
2410                   windowNumber: window->number
2411                   context: GSCurrentContext()
2412                   characters: keys
2413                   charactersIgnoringModifiers: keys
2414                   isARepeat: keyRepeat
2415                   keyCode: keyCode];
2416          return event;
2417        }
2418    }
2419  else
2420    {
2421      /* Now we get the unicode character for the pressed key using our
2422       * internal table.
2423       */
2424      unicode = process_char (keysym, &eventFlags);
2425
2426      /* If that didn't work, we use what X gave us */
2427      if (unicode != 0)
2428        {
2429          keys = [NSString stringWithCharacters: &unicode  length: 1];
2430        }
2431
2432      // Now the same ignoring modifiers, except Shift, ShiftLock, NumLock.
2433      xEvent->xkey.state = (xEvent->xkey.state & (ShiftMask | LockMask
2434                                                  | _num_lock_mask));
2435      ukeys = [context->inputServer lookupStringForEvent: (XKeyEvent *)xEvent
2436                      window: window
2437                      keysym: &keysym];
2438      unicode = process_char (keysym, &eventFlags);
2439      if (unicode != 0)
2440        {
2441          ukeys = [NSString stringWithCharacters: &unicode  length: 1];
2442        }
2443
2444      event = [NSEvent keyEventWithType: eventType
2445                   location: eventLocation
2446                   modifierFlags: eventFlags
2447                   timestamp: (NSTimeInterval)xEvent->xkey.time / 1000.0
2448                   windowNumber: window->number
2449                   context: GSCurrentContext()
2450                   characters: keys
2451                   charactersIgnoringModifiers: ukeys
2452                   isARepeat: keyRepeat
2453                   keyCode: keyCode];
2454
2455      return event;
2456    }
2457}
2458
2459static unichar
2460process_char (KeySym keysym, unsigned *eventModifierFlags)
2461{
2462  switch (keysym)
2463    {
2464      /* NB: Whatever is explicitly put in this conversion table takes
2465         precedence over what is returned by XLookupString.  Not sure
2466         this is a good idea for latin-1 character input. */
2467    case XK_Return:       return NSCarriageReturnCharacter;
2468    case XK_KP_Enter:     return NSEnterCharacter;
2469    case XK_Linefeed:     return NSFormFeedCharacter;
2470    case XK_Tab:          return NSTabCharacter;
2471#ifdef XK_XKB_KEYS
2472    case XK_ISO_Left_Tab: return NSBackTabCharacter;
2473#endif
2474      /* FIXME: The following line ? */
2475    case XK_Escape:       return 0x1b;
2476    case XK_BackSpace:    return NSDeleteCharacter;
2477
2478      /* The following keys need to be reported as function keys */
2479#define XGPS_FUNCTIONKEY \
2480*eventModifierFlags = *eventModifierFlags | NSFunctionKeyMask;
2481
2482    case XK_F1:           XGPS_FUNCTIONKEY return NSF1FunctionKey;
2483    case XK_F2:           XGPS_FUNCTIONKEY return NSF2FunctionKey;
2484    case XK_F3:           XGPS_FUNCTIONKEY return NSF3FunctionKey;
2485    case XK_F4:           XGPS_FUNCTIONKEY return NSF4FunctionKey;
2486    case XK_F5:           XGPS_FUNCTIONKEY return NSF5FunctionKey;
2487    case XK_F6:           XGPS_FUNCTIONKEY return NSF6FunctionKey;
2488    case XK_F7:           XGPS_FUNCTIONKEY return NSF7FunctionKey;
2489    case XK_F8:           XGPS_FUNCTIONKEY return NSF8FunctionKey;
2490    case XK_F9:           XGPS_FUNCTIONKEY return NSF9FunctionKey;
2491    case XK_F10:          XGPS_FUNCTIONKEY return NSF10FunctionKey;
2492    case XK_F11:          XGPS_FUNCTIONKEY return NSF11FunctionKey;
2493    case XK_F12:          XGPS_FUNCTIONKEY return NSF12FunctionKey;
2494    case XK_F13:          XGPS_FUNCTIONKEY return NSF13FunctionKey;
2495    case XK_F14:          XGPS_FUNCTIONKEY return NSF14FunctionKey;
2496    case XK_F15:          XGPS_FUNCTIONKEY return NSF15FunctionKey;
2497    case XK_F16:          XGPS_FUNCTIONKEY return NSF16FunctionKey;
2498    case XK_F17:          XGPS_FUNCTIONKEY return NSF17FunctionKey;
2499    case XK_F18:          XGPS_FUNCTIONKEY return NSF18FunctionKey;
2500    case XK_F19:          XGPS_FUNCTIONKEY return NSF19FunctionKey;
2501    case XK_F20:          XGPS_FUNCTIONKEY return NSF20FunctionKey;
2502    case XK_F21:          XGPS_FUNCTIONKEY return NSF21FunctionKey;
2503    case XK_F22:          XGPS_FUNCTIONKEY return NSF22FunctionKey;
2504    case XK_F23:          XGPS_FUNCTIONKEY return NSF23FunctionKey;
2505    case XK_F24:          XGPS_FUNCTIONKEY return NSF24FunctionKey;
2506    case XK_F25:          XGPS_FUNCTIONKEY return NSF25FunctionKey;
2507    case XK_F26:          XGPS_FUNCTIONKEY return NSF26FunctionKey;
2508    case XK_F27:          XGPS_FUNCTIONKEY return NSF27FunctionKey;
2509    case XK_F28:          XGPS_FUNCTIONKEY return NSF28FunctionKey;
2510    case XK_F29:          XGPS_FUNCTIONKEY return NSF29FunctionKey;
2511    case XK_F30:          XGPS_FUNCTIONKEY return NSF30FunctionKey;
2512    case XK_F31:          XGPS_FUNCTIONKEY return NSF31FunctionKey;
2513    case XK_F32:          XGPS_FUNCTIONKEY return NSF32FunctionKey;
2514    case XK_F33:          XGPS_FUNCTIONKEY return NSF33FunctionKey;
2515    case XK_F34:          XGPS_FUNCTIONKEY return NSF34FunctionKey;
2516    case XK_F35:          XGPS_FUNCTIONKEY return NSF35FunctionKey;
2517    case XK_Delete:       XGPS_FUNCTIONKEY return NSDeleteFunctionKey;
2518    case XK_Home:         XGPS_FUNCTIONKEY return NSHomeFunctionKey;
2519    case XK_Left:         XGPS_FUNCTIONKEY return NSLeftArrowFunctionKey;
2520    case XK_Right:        XGPS_FUNCTIONKEY return NSRightArrowFunctionKey;
2521    case XK_Up:           XGPS_FUNCTIONKEY return NSUpArrowFunctionKey;
2522    case XK_Down:         XGPS_FUNCTIONKEY return NSDownArrowFunctionKey;
2523//  case XK_Prior:        XGPS_FUNCTIONKEY return NSPrevFunctionKey;
2524//  case XK_Next:         XGPS_FUNCTIONKEY return NSNextFunctionKey;
2525    case XK_End:          XGPS_FUNCTIONKEY return NSEndFunctionKey;
2526    case XK_Begin:        XGPS_FUNCTIONKEY return NSBeginFunctionKey;
2527    case XK_Select:       XGPS_FUNCTIONKEY return NSSelectFunctionKey;
2528    case XK_Print:        XGPS_FUNCTIONKEY return NSPrintFunctionKey;
2529    case XK_Execute:      XGPS_FUNCTIONKEY return NSExecuteFunctionKey;
2530    case XK_Insert:       XGPS_FUNCTIONKEY return NSInsertFunctionKey;
2531    case XK_Undo:         XGPS_FUNCTIONKEY return NSUndoFunctionKey;
2532    case XK_Redo:         XGPS_FUNCTIONKEY return NSRedoFunctionKey;
2533    case XK_Menu:         XGPS_FUNCTIONKEY return NSMenuFunctionKey;
2534    case XK_Find:         XGPS_FUNCTIONKEY return NSFindFunctionKey;
2535    case XK_Help:         XGPS_FUNCTIONKEY return NSHelpFunctionKey;
2536    case XK_Break:        XGPS_FUNCTIONKEY return NSBreakFunctionKey;
2537    case XK_Mode_switch:  XGPS_FUNCTIONKEY return NSModeSwitchFunctionKey;
2538    case XK_Scroll_Lock:  XGPS_FUNCTIONKEY return NSScrollLockFunctionKey;
2539    case XK_Pause:        XGPS_FUNCTIONKEY return NSPauseFunctionKey;
2540    case XK_Clear:        XGPS_FUNCTIONKEY return NSClearDisplayFunctionKey;
2541#ifndef NeXT
2542    case XK_Page_Up:      XGPS_FUNCTIONKEY return NSPageUpFunctionKey;
2543    case XK_Page_Down:    XGPS_FUNCTIONKEY return NSPageDownFunctionKey;
2544    case XK_Sys_Req:      XGPS_FUNCTIONKEY return NSSysReqFunctionKey;
2545#endif
2546    case XK_KP_F1:        XGPS_FUNCTIONKEY return NSF1FunctionKey;
2547    case XK_KP_F2:        XGPS_FUNCTIONKEY return NSF2FunctionKey;
2548    case XK_KP_F3:        XGPS_FUNCTIONKEY return NSF3FunctionKey;
2549    case XK_KP_F4:        XGPS_FUNCTIONKEY return NSF4FunctionKey;
2550#ifndef NeXT
2551    case XK_KP_Home:      XGPS_FUNCTIONKEY return NSHomeFunctionKey;
2552    case XK_KP_Left:      XGPS_FUNCTIONKEY return NSLeftArrowFunctionKey;
2553    case XK_KP_Up:        XGPS_FUNCTIONKEY return NSUpArrowFunctionKey;
2554    case XK_KP_Right:     XGPS_FUNCTIONKEY return NSRightArrowFunctionKey;
2555    case XK_KP_Down:      XGPS_FUNCTIONKEY return NSDownArrowFunctionKey;
2556//  case XK_KP_Prior:     return NSPrevFunctionKey;
2557    case XK_KP_Page_Up:   XGPS_FUNCTIONKEY return NSPageUpFunctionKey;
2558//  case XK_KP_Next:      return NSNextFunctionKey;
2559    case XK_KP_Page_Down: XGPS_FUNCTIONKEY return NSPageDownFunctionKey;
2560    case XK_KP_End:       XGPS_FUNCTIONKEY return NSEndFunctionKey;
2561    case XK_KP_Begin:     XGPS_FUNCTIONKEY return NSBeginFunctionKey;
2562    case XK_KP_Insert:    XGPS_FUNCTIONKEY return NSInsertFunctionKey;
2563    case XK_KP_Delete:    XGPS_FUNCTIONKEY return NSDeleteFunctionKey;
2564#endif
2565#undef XGPS_FUNCTIONKEY
2566    default:              return 0;
2567    }
2568}
2569
2570// process_modifier_flags() determines which modifier keys (Command, Control,
2571// Shift,  and so forth) were held down while the event occured.
2572static unsigned int
2573process_modifier_flags(unsigned int state)
2574{
2575  unsigned int eventModifierFlags = 0;
2576
2577  if (_shift_pressed != 0)
2578    eventModifierFlags = eventModifierFlags | NSShiftKeyMask;
2579
2580  if (state & LockMask)
2581    eventModifierFlags = eventModifierFlags | NSAlphaShiftKeyMask;
2582
2583  if (_control_pressed != 0)
2584    eventModifierFlags = eventModifierFlags | NSControlKeyMask;
2585
2586  if (_command_pressed != 0)
2587    eventModifierFlags = eventModifierFlags | NSCommandKeyMask;
2588
2589  if (_alt_pressed != 0)
2590    eventModifierFlags = eventModifierFlags | NSAlternateKeyMask;
2591
2592  if (_help_pressed != 0)
2593    eventModifierFlags = eventModifierFlags | NSHelpKeyMask;
2594
2595  // Other modifiers ignored for now.
2596
2597  return eventModifierFlags;
2598}
2599
2600
2601- (NSDate*) timedOutEvent: (void*)data
2602                     type: (RunLoopEventType)type
2603                  forMode: (NSString*)mode
2604{
2605  return nil;
2606}
2607
2608/* Drag and Drop */
2609- (id <NSDraggingInfo>)dragInfo
2610{
2611  return [XGDragView sharedDragView];
2612}
2613
2614@end
2615
2616@implementation XGServer (XSync)
2617- (BOOL) xSyncMap: (void*)windowHandle
2618{
2619  gswindow_device_t *window = (gswindow_device_t*)windowHandle;
2620
2621  /*
2622   * if the window is not mapped, make sure we have sent all requests to the
2623   * X-server, it may be that our mapping request was buffered.
2624   */
2625  if (window->map_state != IsViewable)
2626    {
2627      XSync(dpy, False);
2628      [self receivedEvent: 0 type: 0 extra: 0 forMode: nil];
2629    }
2630  /*
2631   * If the window is still not mapped, it may be that the window-manager
2632   * intercepted our mapping request, and hasn't dealt with it yet.
2633   * Listen for input for up to a second, in the hope of getting the mapping.
2634   */
2635  if (window->map_state != IsViewable)
2636    {
2637      NSDate *d = [NSDate dateWithTimeIntervalSinceNow: 1.0];
2638      NSRunLoop *l = [NSRunLoop currentRunLoop];
2639      NSString *m = [l currentMode];
2640
2641      while (window->map_state != IsViewable && [d timeIntervalSinceNow] > 0)
2642        {
2643          [l runMode: m beforeDate: d];
2644        }
2645    }
2646  if (window->map_state != IsViewable)
2647    {
2648      NSLog(@"Window still not mapped a second after mapping request made");
2649      return NO;
2650    }
2651  return YES;
2652}
2653@end
2654
2655@implementation XGServer (X11Ops)
2656
2657/*
2658 * Return mouse location in base coords ignoring the event loop
2659 */
2660- (NSPoint) mouselocation
2661{
2662  return [self mouseLocationOnScreen: defScreen window: NULL];
2663}
2664
2665- (NSPoint) mouseLocationOnScreen: (int)screen window: (int *)win
2666{
2667  Window rootWin;
2668  Window childWin;
2669  int currentX;
2670  int currentY;
2671  int winX;
2672  int winY;
2673  unsigned mask;
2674  BOOL ok;
2675  NSPoint p;
2676  int height;
2677  int screen_id;
2678
2679  ok = XQueryPointer (dpy, [self xDisplayRootWindow],
2680                      &rootWin, &childWin, &currentX, &currentY,
2681                      &winX, &winY, &mask);
2682  p = NSMakePoint(-1,-1);
2683  /* FIXME: After multi-monitor support will be implemented `screen` method
2684     parameter doesn't make sense. The `if{}` block should be removed since
2685     we have only one screen and mouse can't be placed on "wrong" screen.
2686     Also actually we need `height` of the whole Xlib screen (defScreen). */
2687  if (ok == False)
2688    {
2689      /* Mouse not on the specified screen_number */
2690      XWindowAttributes attribs;
2691      ok = XGetWindowAttributes(dpy, rootWin, &attribs);
2692      if (ok == False)
2693        {
2694          return p;
2695        }
2696      screen_id = XScreenNumberOfScreen(attribs.screen);
2697      if (screen >= 0 && screen != screen_id)
2698        {
2699          /* Mouse not on the requred screen, return an invalid point */
2700          return p;
2701        }
2702      height = attribs.height;
2703    }
2704  else
2705    {
2706      height = xScreenSize.height;
2707    }
2708  p = NSMakePoint(currentX, height - currentY);
2709  if (win)
2710    {
2711      gswindow_device_t *w = [XGServer _windowForXWindow: childWin];
2712
2713      if (w == NULL)
2714        w = [XGServer _windowForXParent: childWin];
2715      if (w)
2716        *win = w->number;
2717      else
2718        *win = 0;
2719    }
2720  return p;
2721}
2722
2723- (NSEvent*) getEventMatchingMask: (unsigned)mask
2724                       beforeDate: (NSDate*)limit
2725                           inMode: (NSString*)mode
2726                          dequeue: (BOOL)flag
2727{
2728  [self receivedEvent: 0 type: 0 extra: 0 forMode: nil];
2729  return [super getEventMatchingMask: mask
2730                          beforeDate: limit
2731                              inMode: mode
2732                             dequeue: flag];
2733}
2734
2735- (void) discardEventsMatchingMask: (unsigned)mask
2736                       beforeEvent: (NSEvent*)limit
2737{
2738  [self receivedEvent: 0 type: 0 extra: 0 forMode: nil];
2739  [super discardEventsMatchingMask: mask
2740                       beforeEvent: limit];
2741}
2742
2743@end
2744
2745@implementation XGServer (TimeKeeping)
2746// Sync time with X server every 10 seconds
2747#define MAX_TIME_DIFF 10
2748// Regard an X time stamp as valid for half a second
2749#define OUT_DATE_TIME_DIFF 0.5
2750
2751- (void) setLastTime: (Time)last
2752{
2753  if (generic.lastTimeStamp == 0
2754      || generic.baseXServerTime + MAX_TIME_DIFF * 1000 < last)
2755    {
2756      // We have not sync'ed with the clock for at least
2757      // MAX_TIME_DIFF seconds ... so we do it now.
2758      generic.lastTimeStamp = [NSDate timeIntervalSinceReferenceDate];
2759      generic.baseXServerTime = last;
2760    }
2761  else
2762    {
2763      // Optimisation to compute the new time stamp instead.
2764      generic.lastTimeStamp += (last - generic.lastTime) / 1000.0;
2765    }
2766
2767  generic.lastTime = last;
2768}
2769
2770- (Time) lastTime
2771{
2772  // In the case of activation via DO the lastTime is outdated and cannot be used.
2773  if (generic.lastTimeStamp == 0
2774      || ((generic.lastTimeStamp + OUT_DATE_TIME_DIFF)
2775          < [NSDate timeIntervalSinceReferenceDate]))
2776    {
2777      return CurrentTime;
2778    }
2779  else
2780    {
2781      return generic.lastTime;
2782    }
2783}
2784
2785@end
2786
2787