1/* WIN32Server - Implements window handling for MSWindows
2
3   Copyright (C) 2002, 2005 Free Software Foundation, Inc.
4
5   Written by: Fred Kiefer <FredKiefer@gmx.de>
6   Date: March 2002
7   Part of this code have been re-written by:
8   Tom MacSween <macsweent@sympatico.ca>
9   Date August 2005
10
11   This file is part of the GNU Objective C User Interface Library.
12
13   This library is free software; you can redistribute it and/or
14   modify it under the terms of the GNU Lesser General Public
15   License as published by the Free Software Foundation; either
16   version 2 of the License, or (at your option) any later version.
17
18   This library is distributed in the hope that it will be useful,
19   but WITHOUT ANY WARRANTY; without even the implied warranty of
20   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
21   Lesser General Public License for more details.
22
23   You should have received a copy of the GNU Lesser General Public
24   License along with this library; see the file COPYING.LIB.
25   If not, see <http://www.gnu.org/licenses/> or write to the
26   Free Software Foundation, 51 Franklin Street, Fifth Floor,
27   Boston, MA 02110-1301, USA.
28*/
29
30#include "config.h"
31#include <Foundation/NSDebug.h>
32#include <Foundation/NSString.h>
33#include <Foundation/NSArray.h>
34#include <Foundation/NSValue.h>
35#include <Foundation/NSConnection.h>
36#include <Foundation/NSRunLoop.h>
37#include <Foundation/NSTimer.h>
38#include <Foundation/NSUserDefaults.h>
39#include <Foundation/NSException.h>
40#include <AppKit/AppKitExceptions.h>
41#include <AppKit/NSApplication.h>
42#include <AppKit/NSGraphics.h>
43#include <AppKit/NSMenu.h>
44#include <AppKit/NSWindow.h>
45#include <AppKit/NSView.h>
46#include <AppKit/NSEvent.h>
47#include <AppKit/NSCursor.h>
48#include <AppKit/NSText.h>
49#include <AppKit/NSTextField.h>
50#include <AppKit/DPSOperators.h>
51#include <GNUstepGUI/GSTheme.h>
52#include <GNUstepGUI/GSTrackingRect.h>
53
54#include "win32/WIN32Server.h"
55#include "win32/WIN32Geometry.h"
56#ifdef HAVE_WGL
57#include "win32/WIN32OpenGL.h"
58#endif
59
60#ifdef __CYGWIN__
61#include <sys/file.h>
62#endif
63
64#include <math.h>
65
66// To update the cursor..
67static BOOL update_cursor = NO;
68static BOOL should_handle_cursor = NO;
69static NSCursor *current_cursor = nil;
70
71
72// Forward declarations...
73static unsigned int mask_for_keystate(BYTE *keyState);
74
75@interface W32DisplayMonitorInfo : NSObject
76{
77  HMONITOR _hMonitor;
78  RECT     _rect;
79  NSRect   _frame;
80}
81
82- (id)initWithHMonitor:(HMONITOR)hMonitor rect:(LPRECT)lprcMonitor;
83- (HMONITOR)hMonitor;
84- (RECT)rect;
85- (NSRect)frame;
86- (void)setFrame:(NSRect)frame;
87
88@end
89
90@implementation W32DisplayMonitorInfo
91
92- (id)initWithHMonitor:(HMONITOR)hMonitor rect:(LPRECT)lprcMonitor
93{
94  self = [self init];
95  if (self)
96    {
97      CGFloat w = lprcMonitor->right - lprcMonitor->left;
98      CGFloat h = lprcMonitor->bottom - lprcMonitor->top;
99      CGFloat x = lprcMonitor->left;
100      CGFloat y = h - lprcMonitor->bottom;
101
102      _hMonitor = hMonitor;
103      _frame = NSMakeRect(x, y, w, h);
104      memcpy(&_rect, lprcMonitor, sizeof(RECT));
105    }
106  return self;
107}
108
109- (HMONITOR)hMonitor
110{
111  return _hMonitor;
112}
113
114- (RECT)rect
115{
116  return _rect;
117}
118
119- (NSRect)frame
120{
121  return _frame;
122}
123
124- (void)setFrame:(NSRect)frame
125{
126  _frame = frame;
127}
128
129@end
130
131
132static BOOL _enableCallbacks = YES;
133
134static NSEvent *process_key_event(WIN32Server *svr,
135                                  HWND hwnd, WPARAM wParam,
136                                  LPARAM lParam, NSEventType eventType);
137static NSEvent *process_mouse_event(WIN32Server *svr,
138                                    HWND hwnd, WPARAM wParam,
139                                    LPARAM lParam, NSEventType eventType,
140                                    UINT uMsg);
141
142LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg,
143                             WPARAM wParam, LPARAM lParam);
144
145BOOL CALLBACK LoadDisplayMonitorInfo(HMONITOR hMonitor,
146                                    HDC hdcMonitor,
147                                    LPRECT lprcMonitor,
148                                    LPARAM dwData)
149{
150  NSMutableArray        *monitors = (NSMutableArray*)dwData;
151  W32DisplayMonitorInfo *info = [[W32DisplayMonitorInfo alloc] initWithHMonitor:hMonitor rect:lprcMonitor];
152
153  NSDebugLog(@"screen %ld:hdc: %ld frame:top:%ld left:%ld right:%ld bottom:%ld  frame:x:%f y:%f w:%f h:%f\n",
154        [monitors count], (long)hMonitor,
155        lprcMonitor->top, lprcMonitor->left,
156        lprcMonitor->right, lprcMonitor->bottom,
157        [info frame].origin.x, [info frame].origin.y,
158        [info frame].size.width, [info frame].size.height);
159  [monitors addObject:info];
160
161  return TRUE;
162}
163
164
165@implementation WIN32Server
166
167- (BOOL) handlesWindowDecorations
168{
169  return handlesWindowDecorations;
170}
171
172- (void) setHandlesWindowDecorations: (BOOL) b
173{
174  handlesWindowDecorations = b;
175}
176
177- (BOOL) usesNativeTaskbar
178{
179  return usesNativeTaskbar;
180}
181
182- (void) setUsesNativeTaskbar: (BOOL) b
183{
184  usesNativeTaskbar = b;
185}
186
187- (void) callback: (id)sender
188{
189  MSG msg;
190  WINBOOL bRet;
191//NSLog(@"Callback");
192  while ((bRet = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) != 0)
193    {
194      if (msg.message == WM_QUIT)
195        {
196          // Exit the program
197          return;
198        }
199      if (bRet == -1)
200        {
201          // handle the error and possibly exit
202        }
203      else
204        {
205          // Original author disregarded a translate message call here stating
206          // that it would give extra character messages - BUT THIS KILLS IME
207          // MESSAGE PROCESSING!!!!!
208          TranslateMessage(&msg);
209          DispatchMessage(&msg);
210        }
211    }
212}
213
214- (BOOL) hasEvent
215{
216  return (GetQueueStatus(QS_ALLEVENTS) != 0);
217}
218
219- (void) receivedEvent: (void*)data
220                  type: (RunLoopEventType)type
221                 extra: (void*)extra
222               forMode: (NSString*)mode
223{
224#ifdef    __CYGWIN__
225  if (type == ET_RDESC)
226#else
227  if (type == ET_WINMSG)
228#endif
229    {
230      MSG	*m = (MSG*)extra;
231
232      if (m->message == WM_QUIT)
233        {
234          [NSApp terminate: nil];
235          // Exit the program
236          return;
237        }
238      else
239        {
240          TranslateMessage(m);
241          DispatchMessage(m);
242        }
243    }
244//  if (mode != nil) [self callback: mode];
245}
246
247
248- (NSEvent*) getEventMatchingMask: (unsigned)mask
249                       beforeDate: (NSDate*)limit
250                           inMode: (NSString*)mode
251                          dequeue: (BOOL)flag
252{
253//  [self callback: nil];
254  return [super getEventMatchingMask: mask
255                beforeDate: limit
256                inMode: mode
257                dequeue: flag];
258}
259
260- (void) discardEventsMatchingMask: (unsigned)mask
261                       beforeEvent: (NSEvent*)limit
262{
263//  [self callback: nil];
264  [super discardEventsMatchingMask: mask
265         beforeEvent: limit];
266}
267
268
269// server
270
271/* Initialize AppKit backend */
272+ (void) initializeBackend
273{
274  NSDebugLog(@"Initializing GNUstep win32 backend.\n");
275
276  [GSDisplayServer setDefaultServerClass: [WIN32Server class]];
277}
278
279- (void) _initWin32Context
280{
281  WNDCLASSEXW wc;
282  hinstance = (HINSTANCE)GetModuleHandle(NULL);
283
284  // Register the main window class.
285  wc.cbSize = sizeof(wc);
286  wc.style = 0;
287  wc.lpfnWndProc = (WNDPROC) MainWndProc;
288  wc.cbClsExtra = 0;
289  // Keep extra space for each window, for OFF_LEVEL and OFF_ORDERED
290  wc.cbWndExtra = WIN_EXTRABYTES;
291  wc.hInstance = hinstance;
292  wc.hIcon = NULL;//currentAppIcon;
293  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
294  wc.hbrBackground = GetStockObject(WHITE_BRUSH);
295  wc.lpszMenuName =  NULL;
296  wc.lpszClassName = L"GNUstepWindowClass";
297  wc.hIconSm = NULL;//currentAppIcon;
298
299  if (!RegisterClassExW(&wc))
300       return;
301
302  // FIXME We should use GetSysColor to get standard colours from MS Window and
303  // use them in NSColor
304
305  // Should we create a message only window here, so we can get events, even when
306  // no windows are created?
307}
308
309- (void) setupRunLoopInputSourcesForMode: (NSString*)mode
310{
311  NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
312
313#ifdef    __CYGWIN__
314  int fdMessageQueue;
315#define WIN_MSG_QUEUE_FNAME    "/dev/windows"
316
317  // Open a file descriptor for the windows message queue
318  fdMessageQueue = open (WIN_MSG_QUEUE_FNAME, O_RDONLY);
319  if (fdMessageQueue == -1)
320    {
321      NSLog(@"Failed opening %s\n", WIN_MSG_QUEUE_FNAME);
322      exit(1);
323    }
324  [currentRunLoop addEvent: (void*)fdMessageQueue
325                  type: ET_RDESC
326                  watcher: (id<RunLoopEvents>)self
327                  forMode: mode];
328#else
329  [currentRunLoop addEvent: (void*)0
330                  type: ET_WINMSG
331                  watcher: (id<RunLoopEvents>)self
332                  forMode: mode];
333#endif
334}
335
336/**
337
338*/
339- (id) initWithAttributes: (NSDictionary *)info
340{
341//  NSNotificationCenter	*nc = [NSNotificationCenter defaultCenter];
342
343  self = [super initWithAttributes: info];
344
345  if (self)
346    {
347      [self _initWin32Context];
348      [super initWithAttributes: info];
349
350      monitorInfo = [[NSMutableArray alloc] init];
351      EnumDisplayMonitors(NULL, NULL, (MONITORENUMPROC)LoadDisplayMonitorInfo, (LPARAM)monitorInfo);
352
353      [self setupRunLoopInputSourcesForMode: NSDefaultRunLoopMode];
354      [self setupRunLoopInputSourcesForMode: NSConnectionReplyMode];
355      [self setupRunLoopInputSourcesForMode: NSModalPanelRunLoopMode];
356      [self setupRunLoopInputSourcesForMode: NSEventTrackingRunLoopMode];
357
358      [self setHandlesWindowDecorations: YES];
359      [self setUsesNativeTaskbar: YES];
360
361      [GSTheme theme];
362      { // Check user defaults
363	NSUserDefaults	*defs;
364	defs = [NSUserDefaults standardUserDefaults];
365
366	if ([defs objectForKey: @"GSUseWMStyles"])
367	  {
368	    NSWarnLog(@"Usage of 'GSUseWMStyles' as user default option is deprecated. "
369		      @"This option will be ignored in future versions. "
370		      @"You should use 'GSBackHandlesWindowDecorations' option.");
371	    [self setHandlesWindowDecorations: ![defs boolForKey: @"GSUseWMStyles"]];
372	  }
373	if ([defs objectForKey: @"GSUsesWMTaskbar"])
374	  {
375	    NSWarnLog(@"Usage of 'GSUseWMTaskbar' as user default option is deprecated. "
376		      @"This option will be ignored in future versions. "
377		      @"You should use 'GSBackUsesNativeTaskbar' option.");
378	    [self setUsesNativeTaskbar: [defs boolForKey: @"GSUseWMTaskbar"]];
379	  }
380
381	if ([defs objectForKey: @"GSBackHandlesWindowDecorations"])
382	  {
383	    [self setHandlesWindowDecorations:
384	      [defs boolForKey: @"GSBackHandlesWindowDecorations"]];
385	  }
386	if ([defs objectForKey: @"GSBackUsesNativeTaskbar"])
387	  {
388	    [self setUsesNativeTaskbar:
389	      [defs boolForKey: @"GSBackUsesNativeTaskbar"]];
390	  }
391      }
392    }
393  return self;
394}
395
396- (void) _destroyWin32Context
397{
398  UnregisterClass("GNUstepWindowClass", hinstance);
399}
400
401- (void) dealloc
402{
403  [self _destroyWin32Context];
404  RELEASE(monitorInfo);
405  [super dealloc];
406}
407
408- (void) restrictWindow: (int)win toImage: (NSImage*)image
409{
410  //TODO [self subclassResponsibility: _cmd];
411}
412
413static HWND foundWindowHwnd = 0;
414static POINT findWindowAtPoint;
415
416LRESULT CALLBACK windowEnumCallback(HWND hwnd, LPARAM lParam)
417{
418	if (foundWindowHwnd == 0 && hwnd != (HWND)lParam)
419		{
420          RECT	r;
421          GetWindowRect(hwnd, &r);
422
423          if (PtInRect(&r,findWindowAtPoint) && IsWindowVisible(hwnd))
424            {
425				NSWindow *window = GSWindowWithNumber((int)hwnd);
426				if (![window ignoresMouseEvents])
427					foundWindowHwnd = hwnd;
428            }
429        }
430	return true;
431}
432
433- (int) findWindowAt: (NSPoint)screenLocation
434           windowRef: (int*)windowRef
435           excluding: (int)win
436{
437  HWND hwnd;
438  POINT p;
439
440  p = GSScreenPointToMS(screenLocation);
441  /*
442   * This is insufficient: hwnd = WindowFromPoint(p);
443   *
444   * We must look through all windows until we find one
445   * which contains the specified point, does not have
446   * the ignoresMouseEvents property set, and is not
447   * the excluded window. This is done through the
448   * EnumWindows function which makes a call back to us
449   * for each window.
450   */
451    foundWindowHwnd = 0;
452    findWindowAtPoint = p;
453	EnumWindows((WNDENUMPROC)windowEnumCallback, win);
454	hwnd = foundWindowHwnd;
455
456  *windowRef = (int)hwnd;	// Any windows
457
458  return (int)hwnd;
459}
460
461// FIXME: The following methods wont work for multiple screens.
462// However, GetDeviceCaps docs say that on a system with multiple screens,
463// LOGPIXELSX/Y will be the same for all screens, so the following is OK.
464/* Screen information */
465- (NSSize) resolutionForScreen: (int)screen
466{
467  int windowsXRes, windowsYRes;
468  NSSize gnustepRes;
469  HDC hdc;
470
471  hdc = GetDC(NULL);
472  windowsXRes = GetDeviceCaps(hdc, LOGPIXELSX);
473  windowsYRes = GetDeviceCaps(hdc, LOGPIXELSY);
474  ReleaseDC(NULL, hdc);
475
476  // We want to return 72 to indicate no scaling factor, but the default
477  // DPI on Windows is 96. So, multiply the result by (72/96) = 0.75
478
479  gnustepRes = NSMakeSize(0.75 * windowsXRes, 0.75 * windowsYRes);
480  return gnustepRes;
481}
482
483- (NSRect) boundsForScreen: (int)screen
484{
485  if (screen < [monitorInfo count])
486    {
487      return [[monitorInfo objectAtIndex: screen] frame];
488    }
489  return NSZeroRect;
490}
491
492- (HMONITOR) monitorHandleForScreen: (int)screen
493{
494  if (screen < [monitorInfo count])
495    {
496      return [[monitorInfo objectAtIndex: screen] hMonitor];
497    }
498  else
499    {
500      NSWarnMLog(@"invalid screen number: %d", screen);
501      return NULL;
502    }
503}
504
505- (HDC) createHdcForScreen: (int)screen
506{
507  HDC hdc = NULL;
508  HMONITOR hMonitor = [self monitorHandleForScreen: screen];
509
510  if (hMonitor == NULL)
511    {
512      NSWarnMLog(@"error obtaining monitor handle for screen: %d", screen);
513    }
514  else
515    {
516      MONITORINFOEX mix = { 0 };
517      mix.cbSize = sizeof(MONITORINFOEX);
518
519      if (GetMonitorInfo(hMonitor, (LPMONITORINFO)&mix) == 0)
520	{
521          NSWarnMLog(@"error obtaining monitor info for screen: %d status: %d",
522                     screen, GetLastError());
523 	}
524      else
525 	{
526          hdc = CreateDC("DISPLAY", mix.szDevice, NULL, NULL);
527          if (hdc == NULL)
528            {
529              NSWarnMLog(@"error creating HDC for screen: %d - status: %d",
530                         screen, GetLastError());
531            }
532 	}
533    }
534
535  return hdc;
536}
537
538- (void) deleteScreenHdc: (HDC)hdc
539{
540  if (hdc == NULL)
541    {
542      NSWarnMLog(@"HDC is NULL");
543    }
544  else
545    {
546      DeleteDC(hdc);
547    }
548}
549
550- (NSWindowDepth) windowDepthForScreen: (int)screen
551{
552  HDC hdc  = [self createHdcForScreen:screen];
553  int bits = 0;
554
555  if (hdc)
556    {
557      bits = GetDeviceCaps(hdc, BITSPIXEL) / 3;
558      //planes = GetDeviceCaps(hdc, PLANES);
559      //NSLog(@"bits %d planes %d", bits, planes);
560      [self deleteScreenHdc:hdc];
561    }
562  return (_GSRGBBitValue | bits);
563}
564
565- (const NSWindowDepth *) availableDepthsForScreen: (int)screen
566{
567  int		 ndepths = 1;
568  NSZone	*defaultZone = NSDefaultMallocZone();
569  NSWindowDepth	*depths = 0;
570
571  depths = NSZoneMalloc(defaultZone, sizeof(NSWindowDepth)*(ndepths + 1));
572  // FIXME
573  depths[0] = [self windowDepthForScreen: screen];
574  depths[1] = 0;
575
576  return depths;
577}
578
579- (NSArray *) screenList
580{
581  NSInteger       index;
582  NSInteger       nMonitors  = [monitorInfo count];
583  NSMutableArray *screenList = [NSMutableArray arrayWithCapacity:nMonitors];
584  for (index = 0; index < nMonitors; ++index)
585    [screenList addObject:[NSNumber numberWithInt:index]];
586  return [[screenList copy] autorelease];
587}
588
589/**
590   Returns the handle of the module instance.  */
591- (void *) serverDevice
592{
593  return hinstance;
594}
595
596/**
597   As the number of the window is actually is handle we return this.  */
598- (void *) windowDevice: (int)win
599{
600  return (void *)win;
601}
602
603- (void) beep
604{
605  Beep(400, 500);
606}
607
608/*
609  styles are mapped between the two systems
610
611    NSBorderlessWindowMask      0
612    NSTitledWindowMask          1
613    NSClosableWindowMask        2
614    NSMiniaturizableWindowMask  4
615    NSResizableWindowMask       8
616    NSIconWindowMask            64
617    NSMiniWindowMask            128
618
619  NSMenu(style) =  NSTitledWindowMask | NSClosableWindowMask =3;
620*/
621- (DWORD) windowStyleForGSStyle: (unsigned int) style
622{
623  DWORD wstyle = 0;
624
625  if ([self handlesWindowDecorations] == NO)
626    return WS_POPUP | WS_CLIPCHILDREN;
627
628  if (style == 0)
629    {
630      wstyle = WS_POPUP;
631    }
632  else
633    {
634      if ((style & NSTitledWindowMask) == NSTitledWindowMask)
635        wstyle |= WS_CAPTION;
636
637      if ((style & NSClosableWindowMask) == NSClosableWindowMask)
638        wstyle |= WS_CAPTION | WS_SYSMENU;
639
640      if ((style & NSMiniaturizableWindowMask) == NSMiniaturizableWindowMask)
641        wstyle |= WS_MINIMIZEBOX | WS_SYSMENU;
642
643      if ((style & NSResizableWindowMask) == NSResizableWindowMask)
644        wstyle |= WS_SIZEBOX | WS_MAXIMIZEBOX;
645
646      if (((style & NSMiniWindowMask) == NSMiniWindowMask)
647          || ((style & NSIconWindowMask) == NSIconWindowMask))
648        wstyle |= WS_ICONIC;
649
650      if (wstyle == 0)
651        wstyle = WS_POPUP;
652    }
653
654   //NSLog(@"Window wstyle %d for style %d", wstyle, style);
655   return wstyle | WS_CLIPCHILDREN;
656}
657
658- (DWORD) exwindowStyleForGSStyle: (unsigned int) style
659{
660  DWORD estyle = 0;
661
662  if ((style & NSUtilityWindowMask) == NSUtilityWindowMask)
663    {
664      // WS_EX_TOOLWINDOW gives windows a thinner frame, like NSUtilityWindowMask
665      estyle |= WS_EX_TOOLWINDOW;
666    }
667
668  if ([self usesNativeTaskbar])
669    {
670      // We will put all bordered windows except utility windows in the
671      // taskbar. Utility windows don't need to be in the taskbar since
672      // they are in the floating window level, so always visible.
673
674      if (style == NSBorderlessWindowMask)
675        {
676          // WS_EX_TOOLWINDOW also prevents windows from appearing in the taskbar.
677          estyle |= WS_EX_TOOLWINDOW;
678        }
679      else if ((style & NSUtilityWindowMask) == 0)
680        {
681          // WS_EX_APPWINDOW requests that the window appear in the taskbar
682          estyle |= WS_EX_APPWINDOW;
683        }
684   }
685  else /* (NO == [self usesNativeTaskbar]) */
686   {
687      // Prevent all windows from appearing in the taskbar. As an undesired
688      // side effect this will give all windows with frames thin "tool window"
689      // frames. We could also get rid of the taskbar buttons by creating
690      // a hidden window, and setting it as the parent of all other windows,
691      // but that would be more complicated to manage.
692      // See http://msdn.microsoft.com/en-us/library/bb776822(v=VS.85).aspx#Managing_Taskbar_But
693
694      estyle |= WS_EX_TOOLWINDOW;
695    }
696
697  return estyle;
698}
699
700- (void) resizeBackingStoreFor: (HWND)hwnd
701{
702#if (BUILD_GRAPHICS==GRAPHICS_winlib)
703  WIN_INTERN *win = (WIN_INTERN *)GetWindowLong((HWND)hwnd, GWL_USERDATA);
704
705  // FIXME: We should check if the size really did change.
706  if (win->useHDC)
707    {
708      HDC hdc, hdc2;
709      HBITMAP hbitmap;
710      HGDIOBJ old;
711      RECT r;
712
713      old = SelectObject(win->hdc, win->old);
714      DeleteObject(old);
715      DeleteDC(win->hdc);
716      win->hdc = NULL;
717      win->old = NULL;
718
719      GetClientRect((HWND)hwnd, &r);
720      hdc = GetDC((HWND)hwnd);
721      hdc2 = CreateCompatibleDC(hdc);
722      hbitmap = CreateCompatibleBitmap(hdc, r.right - r.left, r.bottom - r.top);
723      win->old = SelectObject(hdc2, hbitmap);
724      win->hdc = hdc2;
725
726      ReleaseDC((HWND)hwnd, hdc);
727
728      // After resizing the backing store, we need to redraw the window
729      win->backingStoreEmpty = YES;
730    }
731#endif
732}
733
734- (BOOL) displayEvent: (unsigned int)uMsg;   // diagnotic filter
735{
736  [self subclassResponsibility: _cmd];
737  return YES;
738}
739
740// main event loop
741
742/*
743 * Reset all of our flags before the next run through the event switch
744 *
745 */
746- (void) setFlagsforEventLoop: (HWND)hwnd
747{
748  flags._eventHandled = NO;
749
750  // future house keeping can go here
751}
752
753- (void) freeCompositionStringForWindow: (HWND)hwnd
754{
755  IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
756
757  // Free any buffer(s)...
758  if (imeInfo->compString)
759    free(imeInfo->compString);
760  imeInfo->compString       = NULL;
761  imeInfo->compStringLength = 0;
762}
763
764- (void) freeReadStringForWindow: (HWND)hwnd
765{
766  IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
767
768  // Free any buffer(s)...
769  if (imeInfo->readString)
770    free(imeInfo->readString);
771  imeInfo->readString       = NULL;
772  imeInfo->readStringLength = 0;
773}
774
775- (void) freeCompositionInfoForWindow: (HWND)hwnd
776{
777  // Clear information...
778  [self freeCompositionStringForWindow: hwnd];
779  [self freeReadStringForWindow: hwnd];
780}
781
782- (void) getCompositionStringForWindow: (HWND)hwnd
783{
784  IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
785  HIMC        immc    = ImmGetContext(hwnd);
786
787  // Current composition string...
788  imeInfo->compStringLength = ImmGetCompositionStringW(immc, GCS_COMPSTR, NULL, 0);
789  imeInfo->compString       = malloc(imeInfo->compStringLength+sizeof(TCHAR));
790  ImmGetCompositionStringW(immc, GCS_COMPSTR, imeInfo->compString, imeInfo->compStringLength);
791
792  // Cleanup...
793  ImmReleaseContext(hwnd, immc);
794}
795
796- (void) getReadStringForWindow: (HWND)hwnd
797{
798  IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
799  HIMC        immc    = ImmGetContext(hwnd);
800
801  // Current read string...
802  imeInfo->readStringLength = ImmGetCompositionStringW(immc, GCS_COMPREADSTR, NULL, 0);
803  imeInfo->readString       = malloc(imeInfo->readStringLength+sizeof(TCHAR));
804  ImmGetCompositionStringW(immc, GCS_COMPREADSTR, imeInfo->readString, imeInfo->readStringLength);
805
806  // Cleanup...
807  ImmReleaseContext(hwnd, immc);
808}
809
810- (void) saveCompositionInfoForWindow: (HWND)hwnd
811{
812  // First, ensure we've cleared out any saved information...
813  [self freeCompositionInfoForWindow: hwnd];
814
815  // Current composition string...
816  [self getCompositionStringForWindow: hwnd];
817
818  // Current read string...
819  [self getReadStringForWindow: hwnd];
820}
821
822- (void) setCompositionStringForWindow: (HWND)hwnd
823{
824  IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
825  HIMC        immc    = ImmGetContext(hwnd);
826
827  // Restore the state...
828  ImmSetCompositionStringW(immc, SCS_SETSTR, imeInfo->compString, imeInfo->compStringLength, NULL, 0);
829
830  // Clear out any saved information...
831  [self freeCompositionInfoForWindow: hwnd];
832
833  // Cleanup...
834  ImmReleaseContext(hwnd, immc);
835}
836
837- (void) setReadStringForWindow: (HWND)hwnd
838{
839  IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
840  HIMC        immc    = ImmGetContext(hwnd);
841
842  // Restore the state...
843  ImmSetCompositionStringW(immc, SCS_SETSTR, NULL, 0, imeInfo->readString, imeInfo->readStringLength);
844
845  // Clear out any saved information...
846  [self freeReadStringForWindow: hwnd];
847
848  // Cleanup...
849  ImmReleaseContext(hwnd, immc);
850}
851
852- (void) restoreCompositionInfoForWindow: (HWND)hwnd
853{
854  // Restore the state...
855  [self setCompositionStringForWindow: hwnd];
856
857  // Restore the read string...
858  [self setReadStringForWindow: hwnd];
859}
860
861- (LRESULT) imnMessage: (HWND)hwnd : (WPARAM)wParam : (LPARAM)lParam
862{
863  HIMC immc = ImmGetContext(hwnd);
864  switch (wParam)
865    {
866    case IMN_CLOSESTATUSWINDOW:
867      {
868	NSDebugLog(@"IMN_CLOSESTATUSWINDOW: hwnd: %p wParam: %p lParam: %p immc: %p\n",
869		    hwnd, wParam, lParam, immc);
870	if (immc)
871	  {
872	    ImmNotifyIME(immc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
873	    HideCaret(hwnd);
874	    DestroyCaret();
875	  }
876	break;
877      }
878
879    case IMN_SETOPENSTATUS:
880      {
881	NSDebugLog(@"IMN_SETOPENSTATUS: hwnd: %p wParam: %p lParam: %p immc: %p\n",
882		    hwnd, wParam, lParam, immc);
883	if (immc)
884	  {
885	    NSDebugLog(@"IMN_SETOPENSTATUS: openstatus: %d\n", ImmGetOpenStatus(immc));
886
887	    IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
888	    if (imeInfo == NULL)
889	      {
890		NSDebugLog(@"IMN_SETOPENSTATUS: IME info pointer is NULL\n");
891	      }
892	    else
893	      {
894		if (ImmGetOpenStatus(immc))
895		  {
896		    if (imeInfo->isOpened == NO)
897		      {
898			// Restore previous information...
899#if defined(IME_SAVERESTORE_COMPOSITIONINFO)
900			[self restoreCompositionInfoForWindow: hwnd];
901#else
902			ImmNotifyIME(immc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
903#endif
904		      }
905		  }
906		else if (imeInfo->isOpened == YES)
907		  {
908		    // Save current information...
909		    [self saveCompositionInfoForWindow: hwnd];
910		  }
911
912		// Save state...
913		imeInfo->isOpened = ImmGetOpenStatus(immc);
914	      }
915
916#if defined(USE_SYSTEM_CARET)
917	    if (ImmGetOpenStatus(immc))
918	      ShowCaret(hwnd);
919	    else
920	      HideCaret(hwnd);
921#endif
922
923#if defined(IME_SETCOMPOSITIONFONT)
924	    {
925	      LOGFONT logFont;
926	      ImmGetCompositionFont(immc, &logFont);
927	      LOGFONT newFont = logFont;
928	      newFont.lfCharSet = ((logFont.lfCharSet == DEFAULT_CHARSET) ? OEM_CHARSET : DEFAULT_CHARSET);
929	      ImmSetCompositionFont(immc, &newFont);
930	      NSDebugLog(@"IMN_SETOPENSTATUS: changing logfont from: %d to: %d\n", logFont.lfCharSet, newFont.lfCharSet);
931	    }
932#endif
933	  }
934	break;
935      }
936
937    case IMN_OPENSTATUSWINDOW:
938      {
939	NSDebugLog(@"IMN_OPENSTATUSWINDOW: hwnd: %p wParam: %p lParam: %p immc: %p\n",
940		    hwnd, wParam, lParam, immc);
941	if (immc)
942	  {
943	    NSDebugLog(@"IMN_OPENSTATUSWINDOW: openstatus: %d\n", ImmGetOpenStatus(immc));
944	    LOGFONT logFont;
945	    {
946	      ImmGetCompositionFont(immc, &logFont);
947	      NSDebugLog(@"IMN_OPENSTATUSWINDOW: logfont - width: %d height: %d\n",
948			  logFont.lfWidth, logFont.lfHeight);
949	    }
950#if defined(USE_SYSTEM_CARET)
951	    CreateCaret(hwnd, NULL, logFont.lfWidth, logFont.lfHeight);
952	    if (ImmGetOpenStatus(immc))
953	      ShowCaret(hwnd);
954	    else
955	      HideCaret(hwnd);
956#endif
957	  }
958	break;
959      }
960
961    case IMN_CHANGECANDIDATE:
962      NSDebugLog(@"IMN_CHANGECANDIDATE: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
963      break;
964
965    case IMN_CLOSECANDIDATE:
966      NSDebugLog(@"IMN_CLOSECANDIDATE: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
967      break;
968
969    case IMN_OPENCANDIDATE:
970      NSDebugLog(@"IMN_OPENCANDIDATE: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
971      break;
972
973    case IMN_SETCONVERSIONMODE:
974      {
975	NSDebugLog(@"IMN_SETCONVERSIONMODE: hwnd: %p wParam: %p lParam: %p immc: %p\n",
976		    hwnd, wParam, lParam, immc);
977	if (immc)
978	  {
979	    DWORD conversion;
980	    DWORD sentence;
981	    if (ImmGetConversionStatus(immc, &conversion, &sentence) == 0)
982	      NSDebugLog(@"IMN_SETCONVERSIONMODE: error getting conversion status: %d\n", GetLastError());
983	    else
984	      NSDebugLog(@"IMN_SETCONVERSIONMODE: conversion: %p sentence: %p\n", conversion, sentence);
985	  }
986	break;
987      }
988
989    case IMN_SETSENTENCEMODE:
990      {
991	NSDebugLog(@"IMN_SETSENTENCEMODE: hwnd: %p wParam: %p lParam: %p immc: %p\n",
992		    hwnd, wParam, lParam, immc);
993	if (immc)
994	  {
995	    DWORD conversion;
996	    DWORD sentence;
997	    if (ImmGetConversionStatus(immc, &conversion, &sentence) == 0)
998	      NSDebugLog(@"IMN_SETSENTENCEMODE: error getting conversion status: %d\n", GetLastError());
999	    else
1000	      NSDebugLog(@"IMN_SETSENTENCEMODE: conversion: %p sentence: %p\n", conversion, sentence);
1001	  }
1002	break;
1003      }
1004
1005    case IMN_SETCANDIDATEPOS:
1006      NSDebugLog(@"IMN_SETCANDIDATEPOS: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1007      break;
1008
1009    case IMN_SETCOMPOSITIONFONT:
1010      {
1011	NSDebugLog(@"IMN_SETCOMPOSITIONFONT: hwnd: %p wParam: %p lParam: %p immc: %p\n",
1012		    hwnd, wParam, lParam, immc);
1013	if (immc)
1014	  {
1015#if defined(IME_SETCOMPOSITIONFONT)
1016	    {
1017	      LOGFONT logFont;
1018	      ImmGetCompositionFont(immc, &logFont);
1019	      NSDebugLog(@"IMN_SETCOMPOSITIONFONT: new character set: %d\n", logFont.lfCharSet);
1020	    }
1021#endif
1022	  }
1023	break;
1024      }
1025
1026    case IMN_SETCOMPOSITIONWINDOW:
1027      NSDebugLog(@"IMN_SETCOMPOSITIONWINDOW: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1028      break;
1029
1030    case IMN_SETSTATUSWINDOWPOS:
1031      NSDebugLog(@"IMN_SETSTATUSWINDOWPOS: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1032      break;
1033
1034    case IMN_GUIDELINE:
1035      NSDebugLog(@"IMN_GUIDELINE: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1036      break;
1037
1038    case IMN_PRIVATE:
1039      NSDebugLog(@"IMN_PRIVATE: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1040      break;
1041
1042    default:
1043      NSDebugLog(@"Unknown IMN message: %p hwnd: %p\n", wParam, hwnd);
1044      break;
1045    }
1046
1047  // Release the IME context...
1048  ImmReleaseContext(hwnd, immc);
1049
1050  return 0;
1051}
1052
1053- (NSEvent*)imeCompositionMessage: (HWND)hwnd : (WPARAM)wParam : (LPARAM)lParam
1054{
1055  NSDebugLog(@"WM_IME_COMPOSITION: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1056  HIMC immc = ImmGetContext(hwnd);
1057  if (immc == 0)
1058    {
1059      NSWarnMLog(@"IMMContext is NULL\n");
1060    }
1061  else if (lParam & GCS_RESULTSTR)
1062    {
1063      // Update our composition string information...
1064      LONG length = ImmGetCompositionStringW(immc, GCS_RESULTSTR, NULL, 0);
1065      NSDebugLog(@"length: %d\n", length);
1066      if (length)
1067	{
1068	  TCHAR composition[length+sizeof(TCHAR)];
1069	  length = ImmGetCompositionStringW(immc, GCS_RESULTSTR, &composition, length);
1070	  {
1071	    int index;
1072	    for (index = 0; index < length; ++index)
1073	      NSDebugLog(@"%2.2X ", composition[index]);
1074	  }
1075	  NSDebugLog(@"composition (uKeys): %@\n",
1076		      [NSString stringWithCharacters: (unichar*)composition length: length]);
1077	}
1078      ImmReleaseContext(hwnd, immc);
1079    }
1080
1081  return 0;
1082}
1083
1084- (LRESULT) imeCharacter: (HWND)hwnd : (WPARAM)wParam : (LPARAM)lParam
1085{
1086  BYTE keyState[256];
1087
1088  // Get the current key states...
1089  GetKeyboardState(keyState);
1090
1091  // key events should go to the key window if we have one (Windows' focus window isn't always appropriate)
1092  int windowNumber = [[NSApp keyWindow] windowNumber];
1093  if (windowNumber == 0)
1094    windowNumber = (int)hwnd;
1095
1096  /* FIXME: How do you guarentee a context is associated with an event? */
1097  NSGraphicsContext *gcontext      = GSCurrentContext();
1098  LONG               ltime         = GetMessageTime();
1099  NSTimeInterval     time          = ltime / 1000.0f;
1100  BOOL               repeat        = (lParam & 0xFFFF) != 0;
1101  unsigned int       eventFlags    = mask_for_keystate(keyState);
1102  DWORD              pos           = GetMessagePos();
1103  NSPoint            eventLocation = MSWindowPointToGS(self, hwnd,  GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
1104  NSString          *keys          = [NSString  stringWithCharacters: (unichar*)&wParam length: 1];
1105  NSString          *ukeys         = [NSString  stringWithCharacters: (unichar*)&wParam  length: 1];
1106
1107  // Create a NSKeyDown message...
1108  NSEvent *ev = [NSEvent keyEventWithType: NSKeyDown
1109                                 location: eventLocation
1110                            modifierFlags: eventFlags
1111                                timestamp: time
1112                             windowNumber: windowNumber
1113                                  context: gcontext
1114                               characters: keys
1115              charactersIgnoringModifiers: ukeys
1116                                isARepeat: repeat
1117                                  keyCode: wParam];
1118
1119  // Post it...
1120  [GSCurrentServer() postEvent: ev atStart: NO];
1121
1122  // then an associated NSKeyUp message...
1123  ev = [NSEvent keyEventWithType: NSKeyUp
1124                        location: eventLocation
1125                   modifierFlags: eventFlags
1126                       timestamp: time
1127                    windowNumber: windowNumber
1128                         context: gcontext
1129                      characters: keys
1130     charactersIgnoringModifiers: ukeys
1131                       isARepeat: repeat
1132                         keyCode: wParam];
1133
1134  // Post it...
1135  [GSCurrentServer() postEvent: ev atStart: NO];
1136
1137  return 0;
1138}
1139
1140- (LRESULT) windowEventProc: (HWND)hwnd : (UINT)uMsg
1141		       : (WPARAM)wParam : (LPARAM)lParam
1142{
1143  NSEvent *ev = nil;
1144
1145  [self setFlagsforEventLoop: hwnd];
1146
1147  switch (uMsg)
1148    {
1149      case WM_MOUSELEAVE:
1150	{
1151	  /* If the cursor leave the window remove the GNUstep cursors, send
1152	   * the appropriate message and tell GNUstep stop handling
1153	   * the cursor.
1154	   */
1155	  NSEvent *e;
1156	  e = [NSEvent otherEventWithType: NSAppKitDefined
1157				 location: NSMakePoint(-1,-1)
1158			    modifierFlags: 0
1159				timestamp: 0
1160			     windowNumber: (int)hwnd
1161				  context: GSCurrentContext()
1162				  subtype: GSAppKitWindowLeave
1163				    data1: 0
1164				    data2: 0];
1165	  [GSCurrentServer() postEvent: e atStart: YES];
1166	  should_handle_cursor = NO;
1167	}
1168	break;
1169      case WM_SIZING:
1170        return [self decodeWM_SIZINGParams: hwnd : wParam : lParam];
1171        break;
1172      case WM_NCCREATE:
1173        return [self decodeWM_NCCREATEParams: wParam : lParam : hwnd];
1174        break;
1175      case WM_NCCALCSIZE:
1176        [self decodeWM_NCCALCSIZEParams: wParam : lParam : hwnd];
1177        break;
1178      case WM_NCACTIVATE:
1179        [self decodeWM_NCACTIVATEParams: wParam : lParam : hwnd];
1180        break;
1181      case WM_NCPAINT:
1182        if ([self handlesWindowDecorations])
1183          [self decodeWM_NCPAINTParams: wParam : lParam : hwnd];
1184        break;
1185      case WM_SHOWWINDOW:
1186      //[self decodeWM_SHOWWINDOWParams: wParam : lParam : hwnd];
1187        break;
1188      case WM_NCDESTROY:
1189        [self decodeWM_NCDESTROYParams: wParam : lParam : hwnd];
1190        break;
1191      case WM_GETTEXT:
1192        [self decodeWM_GETTEXTParams: wParam : lParam : hwnd];
1193        break;
1194      case WM_STYLECHANGING:
1195        break;
1196      case WM_STYLECHANGED:
1197        break;
1198      case WM_GETMINMAXINFO:
1199        return [self decodeWM_GETMINMAXINFOParams: wParam : lParam : hwnd];
1200        break;
1201      case WM_CREATE:
1202        return [self decodeWM_CREATEParams: wParam : lParam : hwnd];
1203        break;
1204      case WM_WINDOWPOSCHANGING:
1205        [self decodeWM_WINDOWPOSCHANGINGParams: wParam : lParam : hwnd];
1206        break;
1207      case WM_WINDOWPOSCHANGED:
1208        [self decodeWM_WINDOWPOSCHANGEDParams: wParam : lParam : hwnd];
1209        break;
1210      case WM_MOVE:
1211        return [self decodeWM_MOVEParams: hwnd : wParam : lParam];
1212        break;
1213      case WM_MOVING:
1214        return [self decodeWM_MOVINGParams: hwnd : wParam : lParam];
1215        break;
1216      case WM_SIZE:
1217        return [self decodeWM_SIZEParams: hwnd : wParam : lParam];
1218        break;
1219      case WM_ENTERSIZEMOVE:
1220        return [self decodeWM_ENTERSIZEMOVEParams: wParam : lParam : hwnd];
1221        break;
1222      case WM_EXITSIZEMOVE:
1223        return [self decodeWM_EXITSIZEMOVEParams: wParam : lParam : hwnd];
1224        break;
1225      case WM_ACTIVATE:
1226        if ((int)lParam !=0)
1227          [self decodeWM_ACTIVEParams: wParam : lParam : hwnd];
1228        break;
1229      case WM_ACTIVATEAPP:
1230        return [self decodeWM_ACTIVEAPPParams: hwnd : wParam : lParam];
1231        break;
1232      case WM_SETFOCUS:
1233        return [self decodeWM_SETFOCUSParams: wParam : lParam : hwnd];
1234        break;
1235      case WM_KILLFOCUS:
1236        if (wParam == (int)hwnd)
1237          return 0;
1238        else
1239          [self decodeWM_KILLFOCUSParams: wParam : lParam : hwnd];
1240        break;
1241      case WM_SETCURSOR:
1242	if (wParam == (int)hwnd)
1243	  {
1244	    // Check if GNUstep should handle the cursor.
1245	    if (should_handle_cursor)
1246	      {
1247		flags._eventHandled = YES;
1248	      }
1249	  }
1250        break;
1251      case WM_QUERYOPEN:
1252        [self decodeWM_QUERYOPENParams: wParam : lParam : hwnd];
1253        break;
1254      case WM_CAPTURECHANGED:
1255        [self decodeWM_CAPTURECHANGEDParams: wParam : lParam : hwnd];
1256        break;
1257      case WM_ERASEBKGND:
1258        return [self decodeWM_ERASEBKGNDParams: wParam : lParam : hwnd];
1259        break;
1260      case WM_PAINT:
1261        [self decodeWM_PAINTParams: (WPARAM)wParam : (LPARAM)lParam : (HWND)hwnd];
1262        break;
1263      case WM_SYNCPAINT:
1264        if ([self handlesWindowDecorations])
1265          [self decodeWM_SYNCPAINTParams: wParam : lParam : hwnd];
1266        break;
1267      case WM_CLOSE:
1268        [self decodeWM_CLOSEParams: wParam : lParam : hwnd];
1269        break;
1270      case WM_DESTROY:
1271        [self decodeWM_DESTROYParams: wParam : lParam : hwnd];
1272        break;
1273      case WM_QUIT:
1274        break;
1275      case WM_USER:
1276        break;
1277      case WM_APP:
1278        break;
1279      case WM_ENTERMENULOOP:
1280	/* If the user open a native contextual menu (a non GNUstep window)
1281	 * send the appropriate message and tell GNUstep stop handling
1282	 * the cursor.
1283	 */
1284	if (wParam)
1285	  {
1286	    NSEvent *e;
1287	    [GSWindowWithNumber((int)hwnd) resetCursorRects];
1288	    e = [NSEvent otherEventWithType: NSAppKitDefined
1289				   location: NSMakePoint(-1,-1)
1290			      modifierFlags: 0
1291				  timestamp: 0
1292			       windowNumber: (int)hwnd
1293				    context: GSCurrentContext()
1294				    subtype: GSAppKitWindowLeave
1295				    data1: 0
1296				      data2: 0];
1297	    [GSCurrentServer() postEvent: e atStart: YES];
1298	    should_handle_cursor = NO;
1299	  }
1300	break;
1301      case WM_EXITMENULOOP:
1302        break;
1303      case WM_INITMENU:
1304        break;
1305      case WM_MENUSELECT:
1306        break;
1307      case WM_ENTERIDLE:
1308        break;
1309      case WM_COMMAND:
1310        [self decodeWM_COMMANDParams: wParam : lParam : hwnd];
1311        break;
1312      case WM_THEMECHANGED:
1313        [self decodeWM_THEMECHANGEDParams: wParam : lParam : hwnd];
1314        break;
1315      case WM_SYSKEYDOWN:  //KEYBOARD
1316        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "SYSKEYDOWN", hwnd);
1317        ev = process_key_event(self, hwnd, wParam, lParam, NSKeyDown);
1318        break;
1319      case WM_SYSKEYUP:  //KEYBOARD
1320        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "SYSKEYUP", hwnd);
1321        ev = process_key_event(self, hwnd, wParam, lParam, NSKeyUp);
1322        break;
1323      case WM_SYSCOMMAND:
1324        [self decodeWM_SYSCOMMANDParams: wParam : lParam : hwnd];
1325        break;
1326      case WM_HELP:
1327        break;
1328      //case WM_GETICON:
1329        //return [self decodeWM_GETICONParams: wParam : lParam : hwnd];
1330        //break;
1331      //case WM_SETICON:
1332        //return [self decodeWM_SETICONParams: wParam : lParam : hwnd];
1333        //break;
1334      case WM_CANCELMODE:
1335        break;
1336      case WM_ENABLE:
1337      case WM_CHILDACTIVATE:
1338        break;
1339      case WM_NULL:
1340        break;
1341
1342      case WM_NCHITTEST: //MOUSE
1343        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "NCHITTEST", hwnd);
1344        break;
1345      case WM_NCMOUSEMOVE: //MOUSE
1346	NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "NCMOUSEMOVE", hwnd);
1347	break;
1348      case WM_NCLBUTTONDOWN:  //MOUSE
1349        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "NCLBUTTONDOWN", hwnd);
1350        break;
1351      case WM_NCLBUTTONUP: //MOUSE
1352        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "NCLBUTTONUP", hwnd);
1353        break;
1354      case WM_MOUSEACTIVATE: //MOUSE
1355        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "MOUSEACTIVATE", hwnd);
1356        break;
1357      case WM_MOUSEMOVE: //MOUSE
1358        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "MOUSEMOVE", hwnd);
1359        ev = process_mouse_event(self, hwnd, wParam, lParam, NSMouseMoved, uMsg);
1360        break;
1361      case WM_LBUTTONDOWN: //MOUSE
1362        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "LBUTTONDOWN", hwnd);
1363        //[self decodeWM_LBUTTONDOWNParams: (WPARAM)wParam : (LPARAM)lParam : (HWND)hwnd];
1364        ev = process_mouse_event(self, hwnd, wParam, lParam, NSLeftMouseDown, uMsg);
1365        break;
1366      case WM_LBUTTONUP: //MOUSE
1367        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "LBUTTONUP", hwnd);
1368        ev = process_mouse_event(self, hwnd, wParam, lParam, NSLeftMouseUp, uMsg);
1369        break;
1370      case WM_LBUTTONDBLCLK: //MOUSE
1371        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "LBUTTONDBLCLK", hwnd);
1372        break;
1373      case WM_MBUTTONDOWN: //MOUSE
1374        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "MBUTTONDOWN", hwnd);
1375        ev = process_mouse_event(self, hwnd, wParam, lParam, NSOtherMouseDown, uMsg);
1376        break;
1377      case WM_MBUTTONUP: //MOUSE
1378        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "MBUTTONUP", hwnd);
1379        ev = process_mouse_event(self, hwnd, wParam, lParam, NSOtherMouseUp, uMsg);
1380        break;
1381      case WM_MBUTTONDBLCLK: //MOUSE
1382        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "MBUTTONDBLCLK", hwnd);
1383        break;
1384      case WM_RBUTTONDOWN: //MOUSE
1385        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "RBUTTONDOWN", hwnd);
1386        ev = process_mouse_event(self, hwnd, wParam, lParam, NSRightMouseDown, uMsg);
1387        break;
1388      case WM_RBUTTONUP: //MOUSE
1389        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "RBUTTONUP", hwnd);
1390        ev = process_mouse_event(self, hwnd, wParam, lParam, NSRightMouseUp, uMsg);
1391        break;
1392      case WM_RBUTTONDBLCLK: //MOUSE
1393        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "RBUTTONDBLCLK", hwnd);
1394        break;
1395      case WM_MOUSEHWHEEL: //MOUSE
1396        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "MOUSEHWHEEL", hwnd);
1397        ev = process_mouse_event(self, hwnd, wParam, lParam, NSScrollWheel, uMsg);
1398        break;
1399
1400      case WM_MOUSEWHEEL: //MOUSE
1401        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "MOUSEWHEEL", hwnd);
1402        ev = process_mouse_event(self, hwnd, wParam, lParam, NSScrollWheel, uMsg);
1403        break;
1404
1405      // WINDOWS IME PROCESSING MESSAGES...
1406      case WM_IME_NOTIFY:
1407        [self imnMessage: hwnd : wParam : lParam];
1408        break;
1409      case WM_IME_REQUEST:
1410        NSDebugLog(@"WM_IME_REQUEST: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1411        break;
1412      case WM_IME_SELECT:
1413        NSDebugLog(@"WM_IME_SELECT: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1414        break;
1415      case WM_IME_SETCONTEXT:
1416        NSDebugLog(@"WM_IME_SETCONTEXT: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1417        break;
1418      case WM_IME_STARTCOMPOSITION:
1419      {
1420        NSDebugLog(@"WM_IME_STARTCOMPOSITION: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1421        IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
1422        if (imeInfo)
1423          imeInfo->isComposing = YES;
1424        break;
1425      }
1426      case WM_IME_ENDCOMPOSITION:
1427      {
1428        NSDebugLog(@"WM_IME_ENDCOMPOSITION: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1429        IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
1430        if (imeInfo)
1431          imeInfo->isComposing = NO;
1432        break;
1433      }
1434      case WM_IME_COMPOSITION:
1435      {
1436        IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
1437        if (imeInfo && imeInfo->isComposing)
1438          ev = [self imeCompositionMessage: hwnd : wParam : lParam];
1439        break;
1440      }
1441      case WM_IME_COMPOSITIONFULL:
1442        NSDebugLog(@"WM_IME_COMPOSITIONFULL: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1443        break;
1444      case WM_IME_KEYDOWN:
1445        NSDebugLog(@"WM_IME_KEYDOWN: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1446        if (wParam == 0xd) // Carriage return...
1447        {
1448          HIMC immc = ImmGetContext(hwnd);
1449          if (immc)
1450          {
1451            // If currently in a composition sequence in the IMM...
1452            ImmNotifyIME(immc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
1453
1454            // Release the context...
1455            ImmReleaseContext(hwnd, immc);
1456          }
1457        }
1458
1459        // Don't pass this message on for processing...
1460        flags._eventHandled = YES;
1461        break;
1462      case WM_IME_KEYUP:
1463        NSDebugLog(@"WM_IME_KEYUP: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1464        break;
1465      case WM_IME_CHAR:
1466        return [self imeCharacter: hwnd : wParam : lParam];
1467        break;
1468
1469      case WM_CHAR:
1470        NSDebugLog(@"WM_CHAR: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1471        break;
1472
1473      case WM_INPUTLANGCHANGEREQUEST:
1474        NSDebugLog(@"WM_INPUTLANGCHANGEREQUEST: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1475        break;
1476      case WM_INPUTLANGCHANGE:
1477        NSDebugLog(@"WM_INPUTLANGCHANGE: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1478        break;
1479
1480      case WM_KEYDOWN:  //KEYBOARD
1481        NSDebugLog(@"WM_KEYDOWN: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1482        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "KEYDOWN", hwnd);
1483        ev = process_key_event(self, hwnd, wParam, lParam, NSKeyDown);
1484        break;
1485      case WM_KEYUP:  //KEYBOARD
1486        NSDebugLog(@"WM_KEYUP: hwnd: %p wParam: %p lParam: %p\n", hwnd, wParam, lParam);
1487        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "KEYUP", hwnd);
1488        ev = process_key_event(self, hwnd, wParam, lParam, NSKeyUp);
1489        break;
1490
1491      case WM_POWERBROADCAST: //SYSTEM
1492        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "POWERBROADCAST", hwnd);
1493        break;
1494      case WM_TIMECHANGE:  //SYSTEM
1495        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "TIMECHANGE", hwnd);
1496        break;
1497      case WM_DEVICECHANGE: //SYSTEM
1498        NSDebugLLog(@"NSEvent", @"Got Message %s for %d", "DEVICECHANGE", hwnd);
1499        break;
1500
1501      default:
1502        // Process all other messages.
1503          NSDebugLLog(@"NSEvent", @"Got unhandled Message %d for %d", uMsg, hwnd);
1504          break;
1505    }
1506
1507    /*
1508     * see if the event was handled in the the main loop or in the
1509     * menu loop.  if eventHandled = YES then we are done and need to
1510     * tell the windows event handler we are finished
1511     */
1512  if (flags._eventHandled == YES)
1513    return 0;
1514
1515  if (ev != nil)
1516    {
1517      [GSCurrentServer() postEvent: ev atStart: NO];
1518      return 0;
1519    }
1520
1521  /*
1522   * We did not care about the event, return it back to the windows
1523   * event handler
1524   */
1525  return DefWindowProcW(hwnd, uMsg, wParam, lParam);
1526}
1527
1528- glContextClass
1529{
1530#ifdef HAVE_WGL
1531  return [Win32GLContext class];
1532#else
1533  return nil;
1534#endif
1535}
1536
1537- glPixelFormatClass
1538{
1539#ifdef HAVE_WGL
1540  return [Win32GLPixelFormat class];
1541#else
1542  return nil;
1543#endif
1544}
1545
1546@end
1547
1548
1549
1550@implementation WIN32Server (WindowOps)
1551
1552/*
1553  styleMask specifies the receiver's style. It can either be
1554  NSBorderlessWindowMask, or it can contain any of the following
1555  options, combined using the C bitwise OR operator: Option Meaning
1556
1557    NSTitledWindowMask          The NSWindow displays a title bar.
1558    NSClosableWindowMask        The NSWindow displays a close button.
1559    NSMiniaturizableWindowMask  The NSWindow displays a miniaturize button.
1560    NSResizableWindowMask       The NSWindow displays a resize bar or border.
1561    NSBorderlessWindowMask
1562
1563    NSBorderlessWindowMask      0
1564    NSTitledWindowMask          1
1565    NSClosableWindowMask        2
1566    NSMiniaturizableWindowMask  4
1567    NSResizableWindowMask       8
1568    NSIconWindowMask            64
1569    NSMiniWindowMask            128
1570
1571  Borderless windows display none of the usual peripheral elements and
1572  are generally useful only for display or caching purposes; you
1573  should normally not need to create them. Also, note that an
1574  NSWindow's style mask should include NSTitledWindowMask if it
1575  includes any of the others.
1576
1577  backingType specifies how the drawing done in the receiver is
1578  buffered by the object's window device: NSBackingStoreBuffered
1579  NSBackingStoreRetained NSBackingStoreNonretained
1580
1581
1582  flag determines whether the Window Server creates a window device
1583  for the new object immediately. If flag is YES, it defers creating
1584  the window until the receiver is moved on screen. All display
1585  messages sent to the NSWindow or its NSViews are postponed until the
1586  window is created, just before it's moved on screen.  Deferring the
1587  creation of the window improves launch time and minimizes the
1588  virtual memory load on the Window Server.  The new NSWindow creates
1589  an instance of NSView to be its default content view.  You can
1590  replace it with your own object by using the setContentView: method.
1591
1592*/
1593
1594- (int) window: (NSRect)frame : (NSBackingStoreType)type : (unsigned int)style
1595              : (int) screen
1596{
1597  HWND hwnd;
1598  RECT r;
1599  DWORD wstyle;
1600  DWORD estyle;
1601
1602  wstyle = [self windowStyleForGSStyle: style];
1603  estyle = [self exwindowStyleForGSStyle: style];
1604
1605  r = GSScreenRectToMS(frame);
1606
1607  NSDebugLLog(@"WTrace", @"window: %@ : %d : %d : %d", NSStringFromRect(frame),
1608              type, style, screen);
1609  NSDebugLLog(@"WTrace", @"         device frame: %d, %d, %d, %d",
1610              r.left, r.top, r.right - r.left, r.bottom - r.top);
1611  hwnd = CreateWindowEx(estyle | WS_EX_LAYERED,
1612                        "GNUstepWindowClass",
1613                        "GNUstepWindow",
1614#if (BUILD_GRAPHICS==GRAPHICS_cairo)
1615                        ((wstyle & WS_POPUP) ? ((wstyle & ~WS_POPUP) | WS_OVERLAPPED) : wstyle),
1616#else
1617                        wstyle,
1618#endif
1619                        r.left,
1620                        r.top,
1621                        r.right - r.left,
1622                        r.bottom - r.top,
1623                        (HWND)NULL,
1624                        (HMENU)NULL,
1625                        hinstance,
1626                        (void*)type);
1627  NSDebugLLog(@"WCTrace", @"         num/handle: %d", hwnd);
1628  if (hwnd == NULL)
1629    {
1630      NSLog(@"CreateWindowEx Failed %d", GetLastError());
1631    }
1632  else
1633    {
1634#if (BUILD_GRAPHICS==GRAPHICS_cairo)
1635      // Borderless window request...
1636      if (wstyle & WS_POPUP)
1637      {
1638        LONG    wstyleOld  = GetWindowLong(hwnd, GWL_STYLE);
1639        LONG    estyleOld  = GetWindowLong(hwnd, GWL_EXSTYLE);
1640        LONG    wstyleNew  = (wstyleOld & ~WS_OVERLAPPEDWINDOW);
1641        LONG    estyleNew  = estyleOld | WS_EX_TOOLWINDOW;
1642
1643        NSDebugMLLog(@"WCTrace", @"wstyles - old: %8.8X new: %8.8X\n",
1644                    wstyleOld, wstyleNew);
1645        NSDebugMLLog(@"WCTrace", @"estyles - old: %8.8X new: %8.8X\n",
1646                    estyleOld, estyleNew);
1647
1648        // Modify window style parameters and update the window information...
1649        SetWindowLong(hwnd, GWL_STYLE, wstyleNew);
1650        SetWindowLong(hwnd, GWL_EXSTYLE, estyleNew);
1651        SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1652                     SWP_FRAMECHANGED | SWP_NOSENDCHANGING | SWP_NOREPOSITION |
1653                     SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1654      }
1655#endif
1656
1657      SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA);
1658
1659      [self _setWindowOwnedByServer: (int)hwnd];
1660    }
1661  return (int)hwnd;
1662}
1663
1664- (void) termwindow: (int) winNum
1665{
1666  NSDebugLLog(@"WCTrace", @"termwindow: %d", winNum);
1667  if (!DestroyWindow((HWND)winNum)) {
1668    NSLog(@"DestroyWindow Failed %d", GetLastError());
1669  }
1670}
1671
1672- (void) stylewindow: (unsigned int)style : (int) winNum
1673{
1674  DWORD wstyle = [self windowStyleForGSStyle: style];
1675  DWORD estyle = [self exwindowStyleForGSStyle: style];
1676
1677  NSAssert([self handlesWindowDecorations],
1678	   @"-stylewindow: : called when [self handlesWindowDecorations] == NO");
1679
1680  NSDebugLLog(@"WTrace", @"stylewindow: %d : %d", style, winNum);
1681  SetWindowLong((HWND)winNum, GWL_STYLE, wstyle);
1682  SetWindowLong((HWND)winNum, GWL_EXSTYLE, estyle);
1683}
1684
1685- (void) setbackgroundcolor: (NSColor *)color : (int)win
1686{
1687}
1688
1689/** Changes window's the backing store to type */
1690- (void) windowbacking: (NSBackingStoreType)type : (int) winNum
1691{
1692  WIN_INTERN *win = (WIN_INTERN *)GetWindowLong((HWND)winNum, GWL_USERDATA);
1693
1694  NSDebugLLog(@"WTrace", @"windowbacking: %d : %d", type, winNum);
1695#if (BUILD_GRAPHICS==GRAPHICS_winlib)
1696  if (win->useHDC)
1697    {
1698      HGDIOBJ old;
1699
1700      old = SelectObject(win->hdc, win->old);
1701      DeleteObject(old);
1702      DeleteDC(win->hdc);
1703      win->hdc = NULL;
1704      win->old = NULL;
1705      win->useHDC = NO;
1706    }
1707
1708  if (type != NSBackingStoreNonretained)
1709    {
1710      HDC hdc, hdc2;
1711      HBITMAP hbitmap;
1712      RECT r;
1713
1714      GetClientRect((HWND)winNum, &r);
1715      hdc = GetDC((HWND)winNum);
1716      hdc2 = CreateCompatibleDC(hdc);
1717      hbitmap = CreateCompatibleBitmap(hdc, r.right - r.left, r.bottom - r.top);
1718      win->old = SelectObject(hdc2, hbitmap);
1719      win->hdc = hdc2;
1720      win->useHDC = YES;
1721      win->backingStoreEmpty = YES;
1722
1723      ReleaseDC((HWND)winNum, hdc);
1724    }
1725  else
1726#endif
1727    {
1728      win->useHDC = NO;
1729      win->hdc = NULL;
1730    }
1731
1732  // Save updated window backing store type...
1733  win->type = type;
1734}
1735
1736- (void) titlewindow: (NSString*)window_title : (int) winNum
1737{
1738  NSDebugLLog(@"WTrace", @"titlewindow: %@ : %d", window_title, winNum);
1739  SetWindowTextW((HWND)winNum, (const unichar*)
1740    [window_title cStringUsingEncoding: NSUnicodeStringEncoding]);
1741}
1742
1743- (void) miniwindow: (int) winNum
1744{
1745  NSDebugLLog(@"WTrace", @"miniwindow: %d", winNum);
1746  ShowWindow((HWND)winNum, SW_MINIMIZE);
1747}
1748
1749/** Returns NO as we don't provide mini windows on MS Windows */
1750- (BOOL) appOwnsMiniwindow
1751{
1752  return NO;
1753}
1754
1755- (void) setWindowdevice: (int)winNum forContext: (NSGraphicsContext *)ctxt
1756{
1757  RECT rect;
1758  float h, l, r, t, b;
1759  NSWindow *window;
1760
1761  NSDebugLLog(@"WTrace", @"windowdevice: %d", winNum);
1762  window = GSWindowWithNumber(winNum);
1763
1764  /* FIXME:
1765   * The windows with autodisplay set to NO aren't displayed correctly on
1766   * Windows, no matter the backing store type used. And trying to redisplay
1767   * these windows here in the server not takes effect. So if the window
1768   * have set autodisplay to NO, we change it to YES before create the window.
1769   * This problem affects the tooltips, but this solution is different to
1770   * the one used in the TestPlant branch. Because that solution involves
1771   * changes in the side of GUI.
1772   */
1773  if (![window isAutodisplay])
1774    {
1775      [window setAutodisplay: YES];
1776    }
1777
1778  GetClientRect((HWND)winNum, &rect);
1779  h = rect.bottom - rect.top;
1780  [self styleoffsets: &l : &r : &t : &b : [window styleMask]];
1781  GSSetDevice(ctxt, (void*)winNum, l, h + b);
1782  DPSinitmatrix(ctxt);
1783  DPSinitclip(ctxt);
1784}
1785
1786- (void) orderwindow: (int) op : (int) otherWin : (int) winNum
1787{
1788  int		flag = 0;
1789  int		foreground = 0;
1790  int		otherLevel;
1791  int		level;
1792  NSWindow *window = GSWindowWithNumber(winNum);
1793
1794  NSDebugLLog(@"WTrace", @"orderwindow: %d : %d : %d", op, otherWin, winNum);
1795
1796  if ([self usesNativeTaskbar])
1797    {
1798      /* When using this policy, we make these changes:
1799         - don't show the application icon window
1800         - Never order out the main menu, just minimize it, so that
1801         when the user clicks on it in the taskbar it will activate the
1802         application.
1803      */
1804      int special;
1805      special = [[NSApp iconWindow] windowNumber];
1806      if (winNum == special)
1807        {
1808          return;
1809        }
1810      special = [[[NSApp mainMenu] window] windowNumber];
1811      if (winNum == special && op == NSWindowOut)
1812        {
1813          ShowWindow((HWND)winNum, SW_MINIMIZE);
1814          return;
1815        }
1816    }
1817
1818  if (op == NSWindowOut)
1819    {
1820      SetWindowLong((HWND)winNum, OFF_ORDERED, 0);
1821      ShowWindow((HWND)winNum, SW_HIDE);
1822      return;
1823    }
1824
1825  if (![window canBecomeMainWindow] && ![window canBecomeKeyWindow])
1826    {
1827      // Bring front, but do not activate, eg - tooltips
1828      flag = SW_SHOWNA;
1829    }
1830  else
1831    {
1832      flag = SW_SHOW;
1833      if (IsIconic((HWND)winNum))
1834        flag = SW_RESTORE;
1835    }
1836
1837  ShowWindow((HWND)winNum, flag);
1838  SetWindowLong((HWND)winNum, OFF_ORDERED, 1);
1839
1840  // Process window leveling...
1841  level = GetWindowLong((HWND)winNum, OFF_LEVEL);
1842
1843  if (otherWin <= 0)
1844    {
1845      if (otherWin == 0 && op == NSWindowAbove)
1846        {
1847          /* This combination means we should move to the top of the current
1848           * window level but stay below the key window, so if we have a key
1849           * window (other than the current window), we store it's id for
1850           * testing later.
1851           */
1852          foreground = (int)GetForegroundWindow();
1853          if (foreground < 0 || foreground == winNum)
1854            {
1855              foreground = 0;
1856            }
1857        }
1858      otherWin = 0;
1859    }
1860
1861  if (otherWin > 0)
1862    {
1863      /* Put this on the same window level as the window we are ordering
1864       * it against.
1865       */
1866      otherLevel = GetWindowLong((HWND)otherWin, OFF_LEVEL);
1867      if (level != otherLevel)
1868        {
1869          NSDebugLLog(@"WTrace",
1870            @"orderwindow: implicitly set level of %d (%d) to that of %d (%d)",
1871            winNum, level, otherWin, otherLevel);
1872                level = otherLevel;
1873          SetWindowLong((HWND)winNum, OFF_LEVEL, level);
1874        }
1875    }
1876
1877  if (level <= NSDesktopWindowLevel)
1878    {
1879      HWND desktop = GetDesktopWindow();
1880
1881      // For desktop level, put this at the bottom of the z-order
1882      SetParent((HWND)winNum, desktop);
1883      otherWin = (int)HWND_BOTTOM;
1884      NSDebugLLog(@"WTrace", @"orderwindow: set %i (%i) to bottom", winNum, level);
1885    }
1886  else if (otherWin == 0 || op == NSWindowAbove)
1887    {
1888      if (otherWin == 0)
1889        {
1890          /* Start searching from bottom of window list...
1891           * The last child of the desktop.
1892           */
1893          otherWin = (int)GetDesktopWindow();
1894          otherWin = (int)GetWindow((HWND)otherWin, GW_CHILD);
1895          if (otherWin > 0)
1896            {
1897              otherWin = (int)GetWindow((HWND)otherWin, GW_HWNDLAST);
1898            }
1899        }
1900      NSDebugLLog(@"WTrace", @"orderwindow: traverse for %d (%d) starting at %d",
1901                  winNum, level, otherWin);
1902      while (otherWin > 0)
1903        {
1904          TCHAR	buf[32];
1905
1906          otherWin = (int)GetNextWindow((HWND)otherWin, GW_HWNDPREV);
1907
1908          /* We only look at gnustep windows (other than the one being
1909           * ordered) to decide where to place our window.
1910           * The assumption is, that if we are ordering a window in,
1911           * we want it to be above any non-gnustep window.
1912           * FIXME ... perhaps we should move all non-gnustep windows
1913           * to be lower than the lowest (excluding gnustep desktop
1914           * level windows I suppose) gnustep window.
1915           */
1916          if (otherWin > 0 && otherWin != winNum
1917            && GetClassName((HWND)otherWin, buf, 32) == 18
1918            && strncmp(buf, "GNUstepWindowClass", 18) == 0)
1919            {
1920              if (GetWindowLong((HWND)otherWin, OFF_ORDERED) == 1)
1921                {
1922                  otherLevel = GetWindowLong((HWND)otherWin, OFF_LEVEL);
1923                  NSDebugLLog(@"WTrace", @"orderwindow: found gnustep window %d (%d)",
1924                              otherWin, otherLevel);
1925                  if (otherLevel >= level)
1926                    {
1927                      if (otherLevel > level)
1928                        {
1929                          /* On windows, there is no notion of levels, so
1930                           * native apps will automatically move to the
1931                                 * very top of the stack (above our alert panels etc)
1932                           *
1933                           * So to cope with this, when we move to the top
1934                           * of a level, we assume there may be native apps
1935                           * above us and we set otherWin=0 to move to the
1936                           * very top of the stack past them.
1937                           *
1938                           * We rely on the fact that we have code in the
1939                           * window positioning notification to rearrange
1940                           * (sort) all the windows into level order if
1941                           * moving this window to the top messes up the
1942                           * level ordering.
1943                           */
1944                          otherWin = 0;
1945                          break;
1946                        }
1947                      if (op == NSWindowBelow || foreground == otherWin)
1948                        {
1949                          break;
1950                        }
1951                    }
1952                }
1953            }
1954        }
1955    }
1956
1957  if (otherWin == 0)
1958    {
1959      otherWin = (int)HWND_TOP;
1960      NSDebugLLog(@"WTrace", @"orderwindow: set %i (%i) to top", winNum, level);
1961    }
1962  else if (otherWin == (int)HWND_BOTTOM)
1963    {
1964      NSDebugLLog(@"WTrace", @"orderwindow: set %i (%i) to bottom", winNum, level);
1965    }
1966  else
1967    {
1968      NSDebugLLog(@"WTrace", @"orderwindow: set %i (%i) below %d", winNum, level, otherWin);
1969    }
1970
1971  SetWindowPos((HWND)winNum, (HWND)otherWin, 0, 0, 0, 0,
1972               SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1973
1974  if (otherWin == (int)HWND_TOP)
1975    {
1976      _enableCallbacks = NO;
1977      if (SetForegroundWindow((HWND)winNum) == 0)
1978        NSDebugMLLog(@"WError", @"SetForegroundWindow error for HWND: %p\n", winNum);
1979      _enableCallbacks = YES;
1980    }
1981  /* For debug log window stack.
1982   */
1983  if (GSDebugSet(@"WTrace") == YES)
1984    {
1985      NSString	*s = @"Window list:\n";
1986
1987      otherWin = (int)GetDesktopWindow();
1988      otherWin = (int)GetWindow((HWND)otherWin, GW_CHILD);
1989      if (otherWin > 0)
1990        {
1991          otherWin = (int)GetWindow((HWND)otherWin, GW_HWNDLAST);
1992        }
1993      while (otherWin > 0)
1994        {
1995          TCHAR	buf[32];
1996
1997          otherWin = (int)GetNextWindow((HWND)otherWin, GW_HWNDPREV);
1998
1999          if (otherWin > 0
2000            && GetClassName((HWND)otherWin, buf, 32) == 18
2001            && strncmp(buf, "GNUstepWindowClass", 18) == 0)
2002            {
2003              if (GetWindowLong((HWND)otherWin, OFF_ORDERED) == 1)
2004                {
2005                  otherLevel = GetWindowLong((HWND)otherWin, OFF_LEVEL);
2006                  s = [s stringByAppendingFormat:
2007                    @"%d (%d)\n", otherWin, otherLevel];
2008                }
2009            }
2010        }
2011      NSDebugLLog(@"WTrace", @"orderwindow: %@", s);
2012    }
2013}
2014
2015- (void) movewindow: (NSPoint)loc : (int)winNum
2016{
2017  POINT p;
2018
2019  NSDebugLLog(@"WTrace", @"movewindow: %@ : %d", NSStringFromPoint(loc),
2020	      winNum);
2021  p = GSWindowOriginToMS((HWND)winNum, loc);
2022
2023  SetWindowPos((HWND)winNum, NULL, p.x, p.y, 0, 0,
2024               SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
2025}
2026
2027- (void) placewindow: (NSRect)frame : (int) winNum
2028{
2029  RECT r;
2030  RECT r2;
2031
2032  NSDebugLLog(@"WTrace", @"placewindow: %@ : %d", NSStringFromRect(frame),
2033              winNum);
2034  r = GSScreenRectToMS(frame);
2035  GetWindowRect((HWND)winNum, &r2);
2036
2037  SetWindowPos((HWND)winNum, NULL,
2038               r.left, r.top, r.right - r.left, r.bottom - r.top,
2039               SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
2040
2041#if (BUILD_GRAPHICS==GRAPHICS_winlib)
2042  WIN_INTERN *win = (WIN_INTERN *)GetWindowLong((HWND)winNum, GWL_USERDATA);
2043
2044  if ((win->useHDC)
2045      && (r.right - r.left != r2.right - r2.left)
2046      && (r.bottom - r.top != r2.bottom - r2.top))
2047    {
2048      HDC hdc, hdc2;
2049      HBITMAP hbitmap;
2050      HGDIOBJ old;
2051
2052      old = SelectObject(win->hdc, win->old);
2053      DeleteObject(old);
2054      DeleteDC(win->hdc);
2055      win->hdc = NULL;
2056      win->old = NULL;
2057
2058      GetClientRect((HWND)winNum, &r);
2059      hdc = GetDC((HWND)winNum);
2060      hdc2 = CreateCompatibleDC(hdc);
2061      hbitmap = CreateCompatibleBitmap(hdc, r.right - r.left, r.bottom - r.top);
2062      win->old = SelectObject(hdc2, hbitmap);
2063      win->hdc = hdc2;
2064
2065      ReleaseDC((HWND)winNum, hdc);
2066    }
2067#endif
2068}
2069
2070- (BOOL) findwindow: (NSPoint)loc : (int) op : (int) otherWin
2071		   : (NSPoint *)floc : (int*) winFound
2072{
2073  return NO;
2074}
2075
2076- (NSRect) windowbounds: (int) winNum
2077{
2078  RECT r;
2079
2080  GetWindowRect((HWND)winNum, &r);
2081  return MSScreenRectToGS(r);
2082}
2083
2084- (void) setwindowlevel: (int) level : (int) winNum
2085{
2086  NSDebugLLog(@"WTrace", @"setwindowlevel: %d : %d", level, winNum);
2087  if (GetWindowLong((HWND)winNum, OFF_LEVEL) != level)
2088    {
2089      SetWindowLong((HWND)winNum, OFF_LEVEL, level);
2090      if (GetWindowLong((HWND)winNum, OFF_ORDERED) == YES)
2091	{
2092          [self orderwindow: NSWindowAbove : 0 : winNum];
2093	}
2094    }
2095}
2096
2097- (int) windowlevel: (int) winNum
2098{
2099  return GetWindowLong((HWND)winNum, OFF_LEVEL);
2100}
2101
2102- (NSArray *) windowlist
2103{
2104  NSMutableArray	*list = [NSMutableArray arrayWithCapacity: 100];
2105  HWND			w;
2106  HWND			next;
2107
2108  w = GetForegroundWindow();	// Try to start with frontmost window
2109  if (w == NULL)
2110    {
2111      w = GetDesktopWindow();	// This should always succeed.
2112      w = GetWindow(w, GW_CHILD);
2113    }
2114
2115  /* Step up to the frontmost window.
2116   */
2117  while ((next = GetNextWindow(w, GW_HWNDPREV)) != NULL)
2118    {
2119      w = next;
2120    }
2121
2122  /* Now walk down the window list populating the array.
2123   */
2124  while (w != NULL)
2125    {
2126      /* Only add windows we own.
2127       * FIXME We should improve the API to support all windows on server.
2128       */
2129      if (GSWindowWithNumber((int)w) != nil)
2130	{
2131	  [list addObject: [NSNumber numberWithInt: (int)w]];
2132	}
2133      w = GetNextWindow(w, GW_HWNDNEXT);
2134    }
2135
2136  return list;
2137}
2138
2139- (int) windowdepth: (int) winNum
2140{
2141  return 0;
2142}
2143
2144/** Set the maximum size of the window */
2145- (void) setmaxsize: (NSSize)size : (int) winNum
2146{
2147  WIN_INTERN *win = (WIN_INTERN *)GetWindowLong((HWND)winNum, GWL_USERDATA);
2148  POINT p;
2149
2150  p.x = size.width;
2151  p.y = size.height;
2152  win->minmax.ptMaxTrackSize = p;
2153
2154  // Disable the maximize box if a maximum size is set
2155  if (size.width < 10000 || size.height < 10000)
2156    {
2157      SetWindowLong((HWND)winNum, GWL_STYLE,
2158          GetWindowLong((HWND)winNum, GWL_STYLE) ^ WS_MAXIMIZEBOX);
2159    }
2160  else
2161    {
2162      SetWindowLong((HWND)winNum, GWL_STYLE,
2163          GetWindowLong((HWND)winNum, GWL_STYLE) | WS_MAXIMIZEBOX);
2164    }
2165}
2166
2167/** Set the minimum size of the window */
2168- (void) setminsize: (NSSize)size : (int) winNum
2169{
2170  WIN_INTERN *win = (WIN_INTERN *)GetWindowLong((HWND)winNum, GWL_USERDATA);
2171  POINT p;
2172
2173  p.x = size.width;
2174  p.y = size.height;
2175  win->minmax.ptMinTrackSize = p;
2176}
2177
2178/** Set the resize incremenet of the window */
2179- (void) setresizeincrements: (NSSize)size : (int) winNum
2180{
2181}
2182/** Causes buffered graphics to be flushed to the screen */
2183- (void) flushwindowrect: (NSRect)rect : (int)winNum
2184{
2185  HWND hwnd = (HWND)winNum;
2186  WIN_INTERN *win = (WIN_INTERN *)GetWindowLong(hwnd, GWL_USERDATA);
2187
2188  if (win)
2189    {
2190#if (BUILD_GRAPHICS==GRAPHICS_winlib)
2191      if (win->useHDC)
2192        {
2193          HDC     hdc = GetDC(hwnd);
2194          RECT    r   = GSWindowRectToMS(self, hwnd, rect);
2195          WINBOOL result;
2196
2197          result = BitBlt(hdc, r.left, r.top,
2198                          (r.right - r.left), (r.bottom - r.top),
2199                          win->hdc, r.left, r.top, SRCCOPY);
2200          if (!result)
2201            {
2202              NSLog(@"Flush window %d %@", hwnd,
2203                    NSStringFromRect(MSWindowRectToGS(self, hwnd, r)));
2204              NSLog(@"Flush window failed with %d", GetLastError());
2205            }
2206          ReleaseDC(hwnd, hdc);
2207        }
2208#elif (BUILD_GRAPHICS==GRAPHICS_cairo)
2209        if (win->surface == NULL)
2210          {
2211            NSWarnMLog(@"NULL surface for window %p", hwnd);
2212          }
2213        else
2214          {
2215            [[GSCurrentContext() class] handleExposeRect: rect forDriver: (void*)win->surface];
2216          }
2217#else
2218#error INVALID build graphics type
2219#endif
2220    }
2221}
2222
2223- (void) styleoffsets: (float *) l : (float *) r : (float *) t : (float *) b
2224		     : (unsigned int) style
2225{
2226  if ([self handlesWindowDecorations])
2227    {
2228      DWORD wstyle = [self windowStyleForGSStyle: style];
2229      DWORD estyle = [self exwindowStyleForGSStyle: style];
2230      RECT rect = {100, 100, 200, 200};
2231
2232      AdjustWindowRectEx(&rect, wstyle, NO, estyle);
2233
2234      *l = 100 - rect.left;
2235      *r = rect.right - 200;
2236      *t = 100 - rect.top;
2237      *b = rect.bottom - 200;
2238      //NSLog(@"Style 0x%X offset %f %f %f %f", wstyle, *l, *r, *t, *b);
2239    }
2240  else
2241    {
2242      /*
2243        If we don't handle decorations, all our windows are going to be
2244        border- and decorationless. In that case, -gui won't call this method,
2245        but we still use it internally.
2246      */
2247      *l = *r = *t = *b = 0.0;
2248    }
2249}
2250
2251- (void) docedited: (int) edited : (int) winNum
2252{
2253}
2254
2255- (void) setinputstate: (int)state : (int)winNum
2256{
2257  if ([self handlesWindowDecorations] == NO)
2258    {
2259      return;
2260    }
2261  if (state == GSTitleBarMain)
2262    {
2263      _enableCallbacks = NO;
2264      SetActiveWindow((HWND)winNum);
2265      _enableCallbacks = YES;
2266    }
2267}
2268
2269/** Forces focus to the window so that all key events are sent to this
2270    window */
2271- (void) setinputfocus: (int) winNum
2272{
2273  NSDebugLLog(@"WTrace", @"setinputfocus: %d", winNum);
2274  NSDebugLLog(@"Focus", @"Setting input focus to %d", winNum);
2275  if (winNum == 0)
2276    {
2277      NSDebugLLog(@"Focus", @" invalid focus window");
2278      return;
2279    }
2280  if (currentFocus == (HWND)winNum)
2281    {
2282      NSDebugLLog(@"Focus", @" window already has focus");
2283      return;
2284    }
2285  desiredFocus = (HWND)winNum;
2286  SetFocus((HWND)winNum);
2287}
2288
2289- (void) setalpha: (float)alpha: (int) win
2290{
2291  if (alpha > 0.99)
2292    {
2293      SetWindowLong((HWND)win, GWL_EXSTYLE,
2294                    GetWindowLong((HWND)win, GWL_EXSTYLE) & ~WS_EX_LAYERED);
2295      RedrawWindow((HWND)win, NULL, NULL,
2296                   RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
2297    }
2298  else
2299    {
2300      SetWindowLong((HWND)win, GWL_EXSTYLE,
2301                    GetWindowLong((HWND)win, GWL_EXSTYLE) | WS_EX_LAYERED);
2302      SetLayeredWindowAttributes((HWND)win, 0, 255 * alpha, LWA_ALPHA);
2303    }
2304}
2305
2306- (NSPoint) mouselocation
2307{
2308  POINT p;
2309
2310  if (!GetCursorPos(&p))
2311    {
2312	  // Try using cursorInfo which should work in more situations
2313	  CURSORINFO cursorInfo;
2314	  cursorInfo.cbSize = sizeof(CURSORINFO);
2315	  if (!GetCursorInfo(&cursorInfo)) {
2316		NSLog(@"GetCursorInfo failed with %d", GetLastError());
2317        return NSZeroPoint;
2318      }
2319	  p = cursorInfo.ptScreenPos;
2320    }
2321
2322  return MSScreenPointToGS(p.x, p.y);
2323}
2324
2325- (NSPoint) mouseLocationOnScreen: (int)screen window: (int *)win
2326{
2327  return [self mouselocation];
2328}
2329
2330- (BOOL) capturemouse: (int) winNum
2331{
2332  NSDebugLLog(@"WTrace", @"capturemouse: %d", winNum);
2333  SetCapture((HWND)winNum);
2334  return YES;
2335}
2336
2337- (void) releasemouse
2338{
2339  NSDebugLLog(@"WTrace", @"releasemouse");
2340  ReleaseCapture();
2341}
2342
2343- (void) hidecursor
2344{
2345  NSDebugLLog(@"WTrace", @"hidecursor");
2346  ShowCursor(NO);
2347}
2348
2349- (void) showcursor
2350{
2351  ShowCursor(YES);
2352}
2353
2354- (void) standardcursor: (int)style : (void **)cid
2355{
2356  HCURSOR hCursor = 0;
2357
2358  NSDebugLLog(@"WTrace", @"standardcursor: %d", style);
2359  switch (style)
2360    {
2361      case GSArrowCursor:
2362        hCursor = LoadCursor(NULL, IDC_ARROW);
2363        break;
2364      case GSIBeamCursor:
2365        hCursor = LoadCursor(NULL, IDC_IBEAM);
2366        break;
2367      case GSCrosshairCursor:
2368        hCursor = LoadCursor(NULL, IDC_CROSS);
2369        break;
2370      case GSPointingHandCursor:
2371        hCursor = LoadCursor(NULL, IDC_HAND);
2372        break;
2373      case GSResizeLeftRightCursor:
2374        hCursor = LoadCursor(NULL, IDC_SIZEWE);
2375        break;
2376      case GSResizeUpDownCursor:
2377        hCursor = LoadCursor(NULL, IDC_SIZENS);
2378        break;
2379      default:
2380        return;
2381    }
2382  *cid = (void*)hCursor;
2383}
2384
2385- (void) imagecursor: (NSPoint)hotp : (NSImage *)image : (void **)cid
2386{
2387  /*
2388    HCURSOR cur;
2389    BYTE *and;
2390    BYTE *xor;
2391    int w, h;
2392
2393    xor = image;
2394    cur = CreateCursor(hinstance, (int)hotp.x, (int)hotp.y,  (int)w, (int)h, and, xor);
2395    *cid = (void*)hCursor;
2396    */
2397}
2398
2399- (void) recolorcursor: (NSColor *)fg : (NSColor *)bg : (void*) cid
2400{
2401  /* FIXME The colour is currently ignored
2402     if (fg != nil)
2403     {
2404     ICONINFO iconinfo;
2405
2406     if (GetIconInfo((HCURSOR)cid, &iconinfo))
2407     {
2408     iconinfo.hbmColor = ;
2409     }
2410     }
2411  */
2412}
2413
2414- (void) setcursor: (void*) cid
2415{
2416  SetCursor((HCURSOR)cid);
2417}
2418
2419- (void) freecursor: (void*) cid
2420{
2421  // This is only allowed on non-shared cursors and we have no way of knowing that.
2422  //DestroyCursor((HCURSOR)cid);
2423}
2424
2425- (void) setParentWindow: (int)parentWin
2426          forChildWindow: (int)childWin
2427{
2428  //SetParent((HWND)childWin, (HWND)parentWin);
2429}
2430
2431- (void) setIgnoreMouse: (BOOL)ignoreMouse : (int)win
2432{
2433  int extendedStyle = GetWindowLong((HWND)win, GWL_EXSTYLE);
2434
2435  if (ignoreMouse)
2436    {
2437      SetWindowLong((HWND)win, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
2438    }
2439  else
2440    {
2441      SetWindowLong((HWND)win, GWL_EXSTYLE, extendedStyle & ~WS_EX_TRANSPARENT);
2442    }
2443}
2444
2445@end
2446
2447static unichar
2448process_char(WPARAM wParam, unsigned *eventModifierFlags)
2449{
2450  switch (wParam)
2451    {
2452      case VK_RETURN: return NSCarriageReturnCharacter;
2453      case VK_TAB:    return NSTabCharacter;
2454      case VK_ESCAPE:  return 0x1b;
2455      case VK_BACK:   return NSDeleteCharacter;
2456
2457	/* The following keys need to be reported as function keys */
2458  #define WIN_FUNCTIONKEY \
2459  *eventModifierFlags = *eventModifierFlags | NSFunctionKeyMask;
2460      case VK_F1: WIN_FUNCTIONKEY return NSF1FunctionKey;
2461      case VK_F2: WIN_FUNCTIONKEY return NSF2FunctionKey;
2462      case VK_F3: WIN_FUNCTIONKEY return NSF3FunctionKey;
2463      case VK_F4: WIN_FUNCTIONKEY return NSF4FunctionKey;
2464      case VK_F5: WIN_FUNCTIONKEY return NSF5FunctionKey;
2465      case VK_F6: WIN_FUNCTIONKEY return NSF6FunctionKey;
2466      case VK_F7: WIN_FUNCTIONKEY return NSF7FunctionKey;
2467      case VK_F8: WIN_FUNCTIONKEY return NSF8FunctionKey;
2468      case VK_F9: WIN_FUNCTIONKEY return NSF9FunctionKey;
2469      case VK_F10: WIN_FUNCTIONKEY return NSF10FunctionKey;
2470      case VK_F11: WIN_FUNCTIONKEY return NSF11FunctionKey;
2471      case VK_F12: WIN_FUNCTIONKEY return NSF12FunctionKey;
2472      case VK_F13: WIN_FUNCTIONKEY return NSF13FunctionKey;
2473      case VK_F14: WIN_FUNCTIONKEY return NSF14FunctionKey;
2474      case VK_F15: WIN_FUNCTIONKEY return NSF15FunctionKey;
2475      case VK_F16: WIN_FUNCTIONKEY return NSF16FunctionKey;
2476      case VK_F17: WIN_FUNCTIONKEY return NSF17FunctionKey;
2477      case VK_F18: WIN_FUNCTIONKEY return NSF18FunctionKey;
2478      case VK_F19: WIN_FUNCTIONKEY return NSF19FunctionKey;
2479      case VK_F20: WIN_FUNCTIONKEY return NSF20FunctionKey;
2480      case VK_F21: WIN_FUNCTIONKEY return NSF21FunctionKey;
2481      case VK_F22: WIN_FUNCTIONKEY return NSF22FunctionKey;
2482      case VK_F23: WIN_FUNCTIONKEY return NSF23FunctionKey;
2483      case VK_F24: WIN_FUNCTIONKEY return NSF24FunctionKey;
2484
2485      case VK_DELETE:      WIN_FUNCTIONKEY return NSDeleteFunctionKey;
2486      case VK_HOME:        WIN_FUNCTIONKEY return NSHomeFunctionKey;
2487      case VK_LEFT:        WIN_FUNCTIONKEY return NSLeftArrowFunctionKey;
2488      case VK_RIGHT:       WIN_FUNCTIONKEY return NSRightArrowFunctionKey;
2489      case VK_UP:          WIN_FUNCTIONKEY return NSUpArrowFunctionKey;
2490      case VK_DOWN:        WIN_FUNCTIONKEY return NSDownArrowFunctionKey;
2491      case VK_PRIOR:       WIN_FUNCTIONKEY return NSPageUpFunctionKey;
2492      case VK_NEXT:        WIN_FUNCTIONKEY return NSPageDownFunctionKey;
2493      case VK_END:         WIN_FUNCTIONKEY return NSEndFunctionKey;
2494      case VK_SELECT:      WIN_FUNCTIONKEY return NSSelectFunctionKey;
2495      case VK_PRINT:       WIN_FUNCTIONKEY return NSPrintFunctionKey;
2496      case VK_SNAPSHOT:    WIN_FUNCTIONKEY return NSPrintScreenFunctionKey;
2497      case VK_EXECUTE:     WIN_FUNCTIONKEY return NSExecuteFunctionKey;
2498      case VK_INSERT:      WIN_FUNCTIONKEY return NSInsertFunctionKey;
2499      case VK_HELP:        WIN_FUNCTIONKEY return NSHelpFunctionKey;
2500      case VK_CANCEL:      WIN_FUNCTIONKEY return NSBreakFunctionKey;
2501      case VK_MODECHANGE:  WIN_FUNCTIONKEY return NSModeSwitchFunctionKey;
2502      case VK_SCROLL:      WIN_FUNCTIONKEY return NSScrollLockFunctionKey;
2503      case VK_PAUSE:       WIN_FUNCTIONKEY return NSPauseFunctionKey;
2504      case VK_OEM_CLEAR:   WIN_FUNCTIONKEY return NSClearDisplayFunctionKey;
2505      case VK_CLEAR:       WIN_FUNCTIONKEY return NSClearLineFunctionKey;
2506  #undef WIN_FUNCTIONKEY
2507      default:
2508	return 0;
2509    }
2510}
2511
2512static unsigned int
2513mask_for_keystate(BYTE *keyState)
2514{
2515  unsigned int eventFlags = 0;
2516  NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
2517  NSString *firstCommand = [defs stringForKey: @"GSFirstCommandKey"];
2518  NSString *firstControl = [defs stringForKey: @"GSFirstControlKey"];
2519  NSString *firstAlt = [defs stringForKey: @"GSFirstAlternateKey"];
2520  NSString *secondCommand = [defs stringForKey: @"GSSecondCommandKey"];
2521  NSString *secondControl = [defs stringForKey: @"GSSecondControlKey"];
2522  NSString *secondAlt = [defs stringForKey: @"GSSecondAlternateKey"];
2523
2524  /* AltGr is mapped to right alt + left control */
2525  if (keyState[VK_RCONTROL] & 128) // && !((keyState[VK_LCONTROL] & 128) && (keyState[VK_RMENU] & 128)))
2526    {
2527      if([@"Control_R" isEqualToString: firstAlt] ||
2528	 [@"Control_R" isEqualToString: secondAlt])
2529	eventFlags |= NSAlternateKeyMask;
2530      else if([@"Control_R" isEqualToString: firstCommand] ||
2531	      [@"Control_R" isEqualToString: secondCommand])
2532	eventFlags |= NSCommandKeyMask;
2533      else
2534	eventFlags |= NSControlKeyMask;
2535    }
2536
2537  if (keyState[VK_SHIFT] & 128)
2538    eventFlags |= NSShiftKeyMask;
2539  if (keyState[VK_CAPITAL] & 128)
2540    eventFlags |= NSShiftKeyMask;
2541
2542  if (keyState[VK_MENU] & 128)
2543    {
2544      if([@"Alt_R" isEqualToString: firstControl] ||
2545	 [@"Alt_R" isEqualToString: secondControl])
2546	eventFlags |= NSControlKeyMask;
2547      else if([@"Alt_R" isEqualToString: firstCommand] ||
2548	      [@"Alt_R" isEqualToString: secondCommand])
2549	eventFlags |= NSCommandKeyMask;
2550      else
2551	eventFlags |= NSAlternateKeyMask;
2552    }
2553
2554  if (keyState[VK_HELP] & 128)
2555    eventFlags |= NSHelpKeyMask;
2556
2557  if ((keyState[VK_LCONTROL] & 128) || (keyState[VK_RWIN] & 128))
2558    {
2559      if([@"Control_L" isEqualToString: firstAlt] ||
2560	 [@"Control_L" isEqualToString: secondAlt])
2561	eventFlags |= NSAlternateKeyMask;
2562      else if([@"Control_L" isEqualToString: firstControl] ||
2563	      [@"Control_L" isEqualToString: secondControl])
2564	eventFlags |= NSControlKeyMask;
2565      else
2566	eventFlags |= NSCommandKeyMask;
2567    }
2568  return eventFlags;
2569}
2570
2571static NSEvent*
2572process_key_event(WIN32Server *svr, HWND hwnd, WPARAM wParam, LPARAM lParam, NSEventType eventType)
2573{
2574  NSEvent *event;
2575  BOOL repeat;
2576  DWORD pos;
2577  NSPoint eventLocation;
2578  unsigned int eventFlags;
2579  NSTimeInterval time;
2580  LONG ltime;
2581  unichar unicode[5];
2582  unsigned int scan;
2583  int result;
2584  BYTE keyState[256];
2585  NSString *keys, *ukeys;
2586  NSGraphicsContext *gcontext;
2587  unichar uChar = 0;
2588
2589  /* FIXME: How do you guarentee a context is associated with an event? */
2590  gcontext = GSCurrentContext();
2591
2592  repeat = (lParam & 0xFFFF) != 0;
2593
2594  pos = GetMessagePos();
2595  eventLocation
2596    = MSWindowPointToGS(svr, hwnd,  GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2597
2598  ltime = GetMessageTime();
2599  time = ltime / 1000.0f;
2600
2601  GetKeyboardState(keyState);
2602  eventFlags = mask_for_keystate(keyState);
2603
2604  switch(wParam)
2605    {
2606      case VK_SHIFT:
2607      case VK_CAPITAL:
2608      case VK_CONTROL:
2609      case VK_MENU:
2610      case VK_HELP:
2611      case VK_NUMLOCK:
2612	eventType = NSFlagsChanged;
2613	break;
2614      case VK_NUMPAD0:
2615      case VK_NUMPAD1:
2616      case VK_NUMPAD2:
2617      case VK_NUMPAD3:
2618      case VK_NUMPAD4:
2619      case VK_NUMPAD5:
2620      case VK_NUMPAD6:
2621      case VK_NUMPAD7:
2622      case VK_NUMPAD8:
2623      case VK_NUMPAD9:
2624	eventFlags |= NSNumericPadKeyMask;
2625	break;
2626      default:
2627	break;
2628    }
2629
2630  if (wParam == VK_PROCESSKEY)
2631  {
2632    // Ignore VK_PROCESSKEY for IME processing...
2633    return 0;
2634  }
2635  else
2636  {
2637    // Ignore if currently in IME composition processing...
2638    IME_INFO_T *imeInfo = (IME_INFO_T*)GetWindowLongPtr(hwnd, IME_INFO);
2639    if (imeInfo && (imeInfo->isComposing))
2640      return 0;
2641    uChar = process_char(wParam, &eventFlags);
2642  }
2643
2644  if (uChar)
2645    {
2646      keys = [NSString  stringWithCharacters: &uChar  length: 1];
2647      ukeys = [NSString  stringWithCharacters: &uChar  length: 1];
2648    }
2649  else
2650    {
2651      BYTE blankKeyState[256];
2652      int i = 0;
2653
2654      // initialize blank key state array....
2655      for(i = 0; i < 256; i++)
2656        blankKeyState[i] = 0;
2657
2658      scan = ((lParam >> 16) & 0xFF);
2659      result = ToUnicodeEx(wParam, scan, keyState, unicode, 5, 0, GetKeyboardLayout(0));
2660      if (result == -1)
2661        {
2662          // A non spacing accent key was found, we still try to use the result
2663          result = 1;
2664        }
2665      keys = [NSString  stringWithCharacters: unicode length: result];
2666
2667      // Now get the characters with a blank keyboard state so that
2668      // no modifiers are applied.
2669      result = ToUnicodeEx(wParam, scan, blankKeyState, unicode, 5, 0, GetKeyboardLayout(0));
2670      //NSLog(@"To Unicode resulted in %d with %d", result, unicode[0]);
2671      if (result == -1)
2672        {
2673          // A non spacing accent key was found, we still try to use the result
2674          result = 1;
2675          NSWarnMLog(@"To Unicode resulted in -1 with: 0x%4.4X\n", unicode[0]);
2676        }
2677      ukeys = [NSString  stringWithCharacters: unicode  length: result];
2678    }
2679
2680  if (eventFlags & NSShiftKeyMask)
2681    ukeys = [ukeys uppercaseString];
2682
2683  // key events should go to the key window if we have one (Windows' focus window isn't always appropriate)
2684  int windowNumber = [[NSApp keyWindow] windowNumber];
2685  if (windowNumber == 0)
2686    windowNumber  = (int)hwnd;
2687
2688  event = [NSEvent keyEventWithType: eventType
2689			   location: eventLocation
2690		      modifierFlags: eventFlags
2691			  timestamp: time
2692		       windowNumber: windowNumber
2693			    context: gcontext
2694			 characters: keys
2695		   charactersIgnoringModifiers: ukeys
2696			  isARepeat: repeat
2697			    keyCode: wParam];
2698
2699  return event;
2700}
2701
2702static NSEvent*
2703process_mouse_event(WIN32Server *svr, HWND hwnd, WPARAM wParam, LPARAM lParam,
2704		    NSEventType eventType, UINT uMsg)
2705{
2706  NSEvent *event;
2707  NSPoint eventLocation;
2708  static NSPoint lastLocation = {0.0, 0.0};
2709  unsigned int eventFlags;
2710  NSTimeInterval time;
2711  LONG ltime;
2712  DWORD tick;
2713  NSGraphicsContext *gcontext;
2714  float deltaX = 0.0, deltaY = 0.0;
2715  static int clickCount = 1;
2716  static LONG lastTime = 0;
2717  int clientX, clientY;
2718/*
2719 * Occasionally the mouse down events are lost ... don't know why.
2720 * So we track the mouse status  and simulate mouse down or up events
2721 * if the button states appear to have changed when we get a move.
2722 */
2723  static BOOL lDown = NO;
2724  static BOOL oDown = NO;
2725  static BOOL rDown = NO;
2726
2727  gcontext = GSCurrentContext();
2728
2729/*
2730 * Some events give screen coordinates - we must convert those to client
2731 * coordinates.
2732 */
2733  if (eventType == NSScrollWheel)
2734    {
2735      POINT point;
2736      point.x = GET_X_LPARAM(lParam);
2737      point.y = GET_Y_LPARAM(lParam);
2738      ScreenToClient(hwnd, &point);
2739      clientX = point.x;
2740      clientY = point.y;
2741    }
2742  else
2743    {
2744      clientX = GET_X_LPARAM(lParam);
2745      clientY = GET_Y_LPARAM(lParam);
2746    }
2747
2748  eventLocation = MSWindowPointToGS(svr, hwnd, clientX, clientY);
2749  ltime = GetMessageTime();
2750  time = ltime / 1000.0f;
2751  tick = GetTickCount();
2752  eventFlags = 0;
2753  if (wParam & MK_CONTROL)
2754    {
2755      eventFlags |= NSControlKeyMask;
2756    }
2757  if (wParam & MK_SHIFT)
2758    {
2759      eventFlags |= NSShiftKeyMask;
2760    }
2761  if (GetKeyState(VK_MENU) < 0)
2762    {
2763      eventFlags |= NSAlternateKeyMask;
2764    }
2765  if (GetKeyState(VK_HELP) < 0)
2766    {
2767      eventFlags |= NSHelpKeyMask;
2768    }
2769  // What about other modifiers?
2770
2771  /* Currently GNUstep only proccess events inside the windows (contentview).
2772   * So we should check if this is the first movement inside the window.
2773   * And should consider also the case when this is the last movement inside
2774   * the window.
2775   */
2776  if (!should_handle_cursor)
2777    {
2778      /* If this is the first movement inside the window, tell GNUstep
2779       * that should handle the cursor and that should check if the
2780       * cursor needs be updated.
2781       */
2782      should_handle_cursor = YES;
2783      update_cursor = YES;
2784
2785      /* We also starts tracking the mouse, so we receive the
2786       * message WM_MOUSELEAVE when the mouse leaves the client area.
2787       */
2788      TRACKMOUSEEVENT tme;
2789      tme.cbSize = sizeof(tme);
2790      tme.dwFlags = TME_LEAVE;
2791      tme.hwndTrack = hwnd;
2792      TrackMouseEvent(&tme);
2793
2794      /* If there are a previous cursor available (maybe a cursor that
2795       * represent a tool) set it as the cursor. If not, set an arrow
2796       * cursor (this is necessary because if the cursor is updated to,
2797       * for example, an I Beam cursor, there will not be a default cursor
2798       * to display when the user moves the mouse over, for example, an
2799       * scrollbar).
2800       */
2801      if (current_cursor != nil)
2802	{
2803	  [current_cursor set];
2804	  current_cursor = nil;
2805	}
2806      else
2807	{
2808	  [[NSCursor arrowCursor] set];
2809	}
2810    }
2811  else
2812    {
2813      /* If the cursor is not associated to a tracking rectangle, not in
2814       * the push/pop stack, save this. We do this for the case when, for
2815       * example, the user choose a tool in a Tools window which sets a
2816       * cursor for the tool and this cursor should be preserved between
2817       * different windows.
2818       */
2819      if ([NSCursor count] == 0 &&
2820	  ![current_cursor isEqual: [NSCursor currentCursor]])
2821	{
2822	  ASSIGN(current_cursor, [NSCursor currentCursor]);
2823	}
2824    }
2825
2826  // Check if we need update the cursor.
2827  if (update_cursor)
2828    {
2829      NSView *subview = nil;
2830      NSWindow *gswin = GSWindowWithNumber((int)hwnd);
2831
2832      subview = [[gswin contentView] hitTest: eventLocation];
2833
2834      if (subview != nil && subview->_rFlags.valid_rects)
2835	{
2836	  NSArray *tr = subview->_cursor_rects;
2837	  NSUInteger count = [tr count];
2838
2839	  // Loop through cursor rectangles
2840	  if (count > 0)
2841	    {
2842	      GSTrackingRect *rects[count];
2843	      NSUInteger i;
2844
2845	      [tr getObjects: rects];
2846
2847	      for (i = 0; i < count; ++i)
2848		{
2849		  GSTrackingRect *r = rects[i];
2850		  BOOL now;
2851
2852		  if ([r isValid] == NO)
2853		    continue;
2854
2855		  /*
2856		   * Check for presence of point in rectangle.
2857		   */
2858		  now = NSMouseInRect(eventLocation, r->rectangle, NO);
2859
2860		  // Mouse inside
2861		  if (now)
2862		    {
2863		      NSEvent *e;
2864
2865		      e = [NSEvent enterExitEventWithType: NSCursorUpdate
2866						 location: eventLocation
2867					    modifierFlags: eventFlags
2868						timestamp: 0
2869					     windowNumber: (int)hwnd
2870						  context: gcontext
2871					      eventNumber: 0
2872					   trackingNumber: (int)YES
2873						 userData: (void*)r];
2874		      [GSCurrentServer() postEvent: e atStart: YES];
2875		      //NSLog(@"Add enter event %@ for view %@ rect %@", e, theView, NSStringFromRect(r->rectangle));
2876		    }
2877		}
2878	    }
2879	}
2880      update_cursor = NO;
2881    }
2882
2883  if (eventType == NSScrollWheel)
2884    {
2885      float delta = GET_WHEEL_DELTA_WPARAM(wParam) / 120.0;
2886      if (uMsg == WM_MOUSEWHEEL)
2887        deltaY = delta;
2888      else
2889        deltaX = delta;
2890      //NSLog(@"Scroll event with deltaX %f deltaY %f", deltaX, deltaY);
2891    }
2892  else if (eventType == NSMouseMoved)
2893    {
2894      NSEvent	*e;
2895      deltaX = eventLocation.x - lastLocation.x;
2896      deltaY = -(eventLocation.y - lastLocation.y);
2897
2898      if (wParam & MK_LBUTTON)
2899        {
2900	  if (lDown == NO)
2901	    {
2902	      e = process_mouse_event(svr, hwnd, wParam, lParam,
2903		NSLeftMouseDown, uMsg);
2904		  if (e != nil)
2905	        [GSCurrentServer() postEvent: e atStart: NO];
2906	    }
2907          eventType = NSLeftMouseDragged;
2908        }
2909      else if (wParam & MK_RBUTTON)
2910        {
2911	  if (lDown == YES)
2912	    {
2913	      e = process_mouse_event(svr, hwnd, wParam, lParam,
2914		NSLeftMouseUp, uMsg);
2915		  if (e != nil)
2916	        [GSCurrentServer() postEvent: e atStart: NO];
2917	    }
2918	  if (rDown == NO)
2919	    {
2920	      e = process_mouse_event(svr, hwnd, wParam, lParam,
2921		NSRightMouseDown, uMsg);
2922		  if (e != nil)
2923	        [GSCurrentServer() postEvent: e atStart: NO];
2924	    }
2925          eventType = NSRightMouseDragged;
2926        }
2927      else if (wParam & MK_MBUTTON)
2928        {
2929	  if (lDown == YES)
2930	    {
2931	      e = process_mouse_event(svr, hwnd, wParam, lParam,
2932		NSLeftMouseUp, uMsg);
2933		  if (e != nil)
2934	        [GSCurrentServer() postEvent: e atStart: NO];
2935	    }
2936	  if (rDown == YES)
2937	    {
2938	      e = process_mouse_event(svr, hwnd, wParam, lParam,
2939		NSRightMouseUp, uMsg);
2940		  if (e != nil)
2941	        [GSCurrentServer() postEvent: e atStart: NO];
2942	    }
2943	  if (oDown == NO)
2944	    {
2945	      e = process_mouse_event(svr, hwnd, wParam, lParam,
2946		NSOtherMouseDown, uMsg);
2947		  if (e != nil)
2948	        [GSCurrentServer() postEvent: e atStart: NO];
2949	    }
2950          eventType = NSOtherMouseDragged;
2951        }
2952      else
2953	{
2954	  if (lDown == YES)
2955	    {
2956	      e = process_mouse_event(svr, hwnd, wParam, lParam,
2957		NSLeftMouseUp, uMsg);
2958		  if (e != nil)
2959	        [GSCurrentServer() postEvent: e atStart: NO];
2960	    }
2961	  if (rDown == YES)
2962	    {
2963	      e = process_mouse_event(svr, hwnd, wParam, lParam,
2964		NSRightMouseUp, uMsg);
2965		  if (e != nil)
2966	        [GSCurrentServer() postEvent: e atStart: NO];
2967	    }
2968	  if (oDown == YES)
2969	    {
2970	      e = process_mouse_event(svr, hwnd, wParam, lParam,
2971		NSOtherMouseUp, uMsg);
2972		  if (e != nil)
2973	        [GSCurrentServer() postEvent: e atStart: NO];
2974	    }
2975	}
2976    }
2977  else if ((eventType == NSLeftMouseDown)
2978    || (eventType == NSRightMouseDown)
2979    || (eventType == NSOtherMouseDown))
2980    {
2981      // It seems Windows generates duplicate mouse down events on first click in a window
2982      if (ltime == lastTime) // duplicate event has identical time
2983	return nil; // ignore it
2984
2985      static NSPoint lastClick = {0.0, 0.0};
2986
2987      if (lastTime + GetDoubleClickTime() > ltime
2988          && fabs(eventLocation.x - lastClick.x) < GetSystemMetrics(SM_CXDOUBLECLK)
2989          && fabs(eventLocation.y - lastClick.y) < GetSystemMetrics(SM_CYDOUBLECLK))
2990        {
2991          clickCount += 1;
2992        }
2993      else
2994        {
2995          clickCount = 1;
2996        }
2997      lastTime = ltime;
2998      lastClick = eventLocation;
2999
3000      SetCapture(hwnd); // capture the mouse to get mouse moved events outside of window
3001    }
3002  else if ( ((eventType == NSLeftMouseUp)
3003    || (eventType == NSRightMouseUp)
3004    || (eventType == NSOtherMouseUp))
3005	&& !((wParam & MK_LBUTTON) || (wParam & MK_MBUTTON) || (wParam & MK_RBUTTON)) )
3006	{
3007	  ReleaseCapture(); // release capture when all mouse buttons are up
3008	}
3009
3010  if (eventType == NSLeftMouseDown) lDown = YES;
3011  if (eventType == NSRightMouseDown) rDown = YES;
3012  if (eventType == NSOtherMouseDown) oDown = YES;
3013  if (eventType == NSLeftMouseUp) lDown = NO;
3014  if (eventType == NSRightMouseUp) rDown = NO;
3015  if (eventType == NSOtherMouseUp) oDown = NO;
3016
3017  if (eventType == NSMouseMoved) {
3018    static HWND lastHwnd = 0;
3019	if (hwnd == lastHwnd && NSEqualPoints(eventLocation, lastLocation))
3020	  return nil; // mouse hasn't actually moved -- don't generate another event
3021	// record window and location of this event, to check next mouseMoved event against
3022	lastHwnd = hwnd;
3023  }
3024
3025  lastLocation = eventLocation;
3026
3027  event = [NSEvent mouseEventWithType: eventType
3028			     location: eventLocation
3029			modifierFlags: eventFlags
3030			    timestamp: time
3031			 windowNumber: (int)hwnd
3032			      context: gcontext
3033			  eventNumber: tick
3034			   clickCount: clickCount
3035			     pressure: 1.0
3036			 buttonNumber: 0 /* FIXME */
3037			       deltaX: deltaX
3038			       deltaY: deltaY
3039			       deltaZ: 0.];
3040
3041  return event;
3042}
3043
3044
3045LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg,
3046			     WPARAM wParam, LPARAM lParam)
3047{
3048  WIN32Server	*ctxt = (WIN32Server *)GSCurrentServer();
3049
3050  if(_enableCallbacks == NO)
3051    {
3052      return (LRESULT)NULL;
3053    }
3054
3055  return [ctxt windowEventProc: hwnd : uMsg : wParam : lParam];
3056}
3057
3058