Create FUNDING.yml
[wdl/wdl-ol.git] / WDL / swell / swell-kb.mm
blob02ba23d575b4b509b0cbd034cd17e109fb966e9e
1 /* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
2    Copyright (C) 2006 and later, Cockos, Inc.
4     This software is provided 'as-is', without any express or implied
5     warranty.  In no event will the authors be held liable for any damages
6     arising from the use of this software.
8     Permission is granted to anyone to use this software for any purpose,
9     including commercial applications, and to alter it and redistribute it
10     freely, subject to the following restrictions:
12     1. The origin of this software must not be misrepresented; you must not
13        claim that you wrote the original software. If you use this software
14        in a product, an acknowledgment in the product documentation would be
15        appreciated but is not required.
16     2. Altered source versions must be plainly marked as such, and must not be
17        misrepresented as being the original software.
18     3. This notice may not be removed or altered from any source distribution.
19   
21     This file provides basic key and mouse cursor querying, as well as a mac key to windows key translation function.
23   */
25 #ifndef SWELL_PROVIDED_BY_APP
27 #include "swell.h"
28 #include "swell-dlggen.h"
29 #import <Cocoa/Cocoa.h>
30 #import <Carbon/Carbon.h>
34 static int MacKeyCodeToVK(int code)
36   switch (code)
37   {
38     case 51: return VK_BACK;
39     case 65: return VK_DECIMAL;
40     case 67: return VK_MULTIPLY;
41     case 69: return VK_ADD;
42     case 71: return VK_NUMLOCK;
43     case 75: return VK_DIVIDE;
44     case 76: return VK_RETURN|0x8000;
45     case 78: return VK_SUBTRACT;
46     case 81: return VK_SEPARATOR;
47     case 82: return VK_NUMPAD0;
48     case 83: return VK_NUMPAD1;
49     case 84: return VK_NUMPAD2;
50     case 85: return VK_NUMPAD3;
51     case 86: return VK_NUMPAD4;
52     case 87: return VK_NUMPAD5;
53     case 88: return VK_NUMPAD6;
54     case 89: return VK_NUMPAD7;
55     case 91: return VK_NUMPAD8;
56     case 92: return VK_NUMPAD9;
57     case 96: return VK_F5;
58     case 97: return VK_F6;
59     case 98: return VK_F7;
60     case 99: return VK_F3;
61     case 100: return VK_F8;
62     case 101: return VK_F9;
63     case 109: return VK_F10;
64     case 103: return VK_F11;
65     case 111: return VK_F12;
66     case 114: return VK_INSERT;
67     case 115: return VK_HOME;
68     case 117: return VK_DELETE;
69     case 116: return VK_PRIOR;
70     case 118: return VK_F4;
71     case 119: return VK_END;
72     case 120: return VK_F2;
73     case 121: return VK_NEXT;
74     case 122: return VK_F1;
75     case 123: return VK_LEFT;
76     case 124: return VK_RIGHT;
77     case 125: return VK_DOWN;
78     case 126: return VK_UP;
79     case 0x69: return VK_F13;
80     case 0x6B: return VK_F14;
81     case 0x71: return VK_F15;
82     case 0x6A: return VK_F16;
83   }
84   return 0;
87 bool IsRightClickEmulateEnabled();
89 #ifdef MAC_OS_X_VERSION_10_5
91 static int charFromVcode(int keyCode) // used for getting the root char (^, `) from dead keys on other keyboards,
92                                        // only used when using MacKeyToWindowsKeyEx() with mode=1, for now 
94   static char loaded;
95   static TISInputSourceRef (*_TISCopyCurrentKeyboardInputSource)( void);
96   static void* (*_TISGetInputSourceProperty) ( TISInputSourceRef inputSource, CFStringRef propertyKey);
98   if (!loaded)
99   {
100     loaded++;
101     CFBundleRef b = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon"));
102     if (b)
103     {
104       *(void **)&_TISGetInputSourceProperty = CFBundleGetFunctionPointerForName(b,CFSTR("TISGetInputSourceProperty"));
105       *(void **)&_TISCopyCurrentKeyboardInputSource = CFBundleGetFunctionPointerForName(b,CFSTR("TISCopyCurrentKeyboardInputSource"));
106     }
107   }
108   if (!_TISCopyCurrentKeyboardInputSource || !_TISGetInputSourceProperty) return 0;
109   
110   TISInputSourceRef currentKeyboard = _TISCopyCurrentKeyboardInputSource();
111   CFDataRef uchr = (CFDataRef)_TISGetInputSourceProperty(currentKeyboard, CFSTR("TISPropertyUnicodeKeyLayoutData"));
112   const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
114   if(keyboardLayout)
115   {
116     UInt32 deadKeyState = 0;
117     UniCharCount maxStringLength = 255;
118     UniCharCount actualStringLength = 0;
119     UniChar unicodeString[maxStringLength];
121     OSStatus status = UCKeyTranslate(keyboardLayout,
122                                      keyCode, kUCKeyActionDown, 0,
123                                      LMGetKbdType(), 0,
124                                      &deadKeyState,
125                                      maxStringLength,
126                                      &actualStringLength, unicodeString);
128     if (actualStringLength == 0 && deadKeyState)
129     {
130         status = UCKeyTranslate(keyboardLayout,
131                                          kVK_Space, kUCKeyActionDown, 0,
132                                          LMGetKbdType(), 0,
133                                          &deadKeyState,
134                                          maxStringLength,
135                                          &actualStringLength, unicodeString);   
136     }
137     if(actualStringLength > 0 && status == noErr) return unicodeString[0]; 
138   }
139   return 0;
141 #endif
143 int SWELL_MacKeyToWindowsKeyEx(void *nsevent, int *flags, int mode)
145   NSEvent *theEvent = (NSEvent *)nsevent;
146   if (!theEvent) theEvent = [NSApp currentEvent];
148   const NSInteger mod=[theEvent modifierFlags];
149     
150   int flag=0;
151   if (mod & NSShiftKeyMask) flag|=FSHIFT;
152   if (mod & NSCommandKeyMask) flag|=FCONTROL; // todo: this should be command once we figure it out
153   if (mod & NSAlternateKeyMask) flag|=FALT;
154   if ((mod&NSControlKeyMask) && !IsRightClickEmulateEnabled()) flag|=FLWIN;
155     
156   int rawcode=[theEvent keyCode];
158   int code=MacKeyCodeToVK(rawcode);
159   if (!code)
160   {
161     NSString *str=NULL;
162     if (mode == 1) str=[theEvent characters];
164     if (!str || ![str length]) str=[theEvent charactersIgnoringModifiers];
166     if (!str || ![str length]) 
167     {
168     #ifdef MAC_OS_X_VERSION_10_5
169       if (mode==1) code=charFromVcode(rawcode);
170     #endif
171       if (!code)
172       {
173         code = 1024+rawcode; // raw code
174         flag|=FVIRTKEY;
175       }
176     }
177     else
178     {
179       code=[str characterAtIndex:0];
180       if (code >= NSF1FunctionKey && code <= NSF24FunctionKey)
181       {
182         flag|=FVIRTKEY;
183         code += VK_F1 - NSF1FunctionKey;
184       }
185       else 
186       {
187         if (code >= 'a' && code <= 'z') code+='A'-'a';
188         if (code == 25 && (flag&FSHIFT)) code=VK_TAB;
189         if (isalnum(code)||code==' ' || code == '\r' || code == '\n' || code ==27 || code == VK_TAB) flag|=FVIRTKEY;
190       }
191     }
192   }
193   else
194   {
195     flag|=FVIRTKEY;
196     if (code==8) code='\b';
197   }
199   if (!(flag&FVIRTKEY)) flag&=~FSHIFT;
200   
201   if (flags) *flags=flag;
202   return code;
205 int SWELL_MacKeyToWindowsKey(void *nsevent, int *flags)
207   if (!nsevent) return 0;
208   return SWELL_MacKeyToWindowsKeyEx(nsevent,flags,0);
211 int SWELL_KeyToASCII(int wParam, int lParam, int *newflags)
213   if (wParam >= '0' && wParam <= '9' && lParam == (FSHIFT|FVIRTKEY))
214   {
215     *newflags = lParam&~(FSHIFT|FVIRTKEY);
216     if (!(lParam & (FCONTROL|FLWIN))) switch (wParam) 
217     {
218       case '1': return '!';
219       case '2': return '@';
220       case '3': return '#';
221       case '4': return '$';
222       case '5': return '%';
223       case '6': return '^';
224       case '7': return '&';
225       case '8': return '*';
226       case '9': return '(';
227       case '0': return ')';      
228     }
229   }
230   return 0;
234 WORD GetAsyncKeyState(int key)
236   CGEventFlags state=0;
237   if (key == VK_LBUTTON || key == VK_RBUTTON || key == VK_MBUTTON)
238   {
239     state=GetCurrentEventButtonState();
240   }
241   else    
242   {
243     state=CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState);
244   }
246   if ((key == VK_LBUTTON && (state&1)) ||
247       (key == VK_RBUTTON && (state&2)) ||
248       (key == VK_MBUTTON && (state&4)) ||      
249       (key == VK_SHIFT && (state&kCGEventFlagMaskShift)) ||
250       (key == VK_CONTROL && (state&kCGEventFlagMaskCommand)) ||
251       (key == VK_MENU && (state&kCGEventFlagMaskAlternate)) ||
252       (key == VK_LWIN && !IsRightClickEmulateEnabled() && (state&kCGEventFlagMaskControl)))
253   {
254     return 0x8000;
255   }
256   
257   return 0;
261 static SWELL_CursorResourceIndex *SWELL_curmodule_cursorresource_head;
263 static NSCursor* MakeCursorFromData(unsigned char* data, int hotspot_x, int hotspot_y)
265   NSCursor *c=NULL;
266   NSBitmapImageRep* bmp = [[NSBitmapImageRep alloc] 
267     initWithBitmapDataPlanes:0
268     pixelsWide:16
269     pixelsHigh:16
270     bitsPerSample:8
271     samplesPerPixel:2  
272     hasAlpha:YES
273     isPlanar:NO 
274     colorSpaceName:NSCalibratedWhiteColorSpace
275     bytesPerRow:(16*2)
276     bitsPerPixel:16]; 
277   
278   if (bmp)
279   {
280     unsigned char* p = [bmp bitmapData];
281     if (p)
282     {  
283       int i;
284       for (i = 0; i < 16*16; ++i)
285       {
286         // tried 4 bits per sample and memcpy, didn't work
287         p[2*i] = (data[i]&0xF0) | data[i]>>4;
288         p[2*i+1] = (data[i]<<4) | (data[i]&0xf);
289       }
290   
291       NSImage *img = [[NSImage alloc] init];
292       if (img)
293       {
294         [img addRepresentation:bmp];  
295         NSPoint hs = NSMakePoint(hotspot_x, hotspot_y);
296         c = [[NSCursor alloc] initWithImage:img hotSpot:hs];
297         [img release];
298       }   
299     }
300     [bmp release];
301   }
302   return c;
305 static NSCursor* MakeSWELLSystemCursor(const char *id)
307   // bytemaps are (white<<4)|(alpha)
308   const unsigned char B = 0xF;
309   const unsigned char W = 0xFF;
310   const unsigned char G = 0xF8;
311   
312   static NSCursor* carr[4] = { 0, 0, 0, 0 };
313   
314   NSCursor** pc=0;
315   if (id == IDC_SIZEALL) pc = &carr[0];
316   else if (id == IDC_SIZENWSE) pc = &carr[1];
317   else if (id == IDC_SIZENESW) pc = &carr[2];
318   else if (id == IDC_NO) pc = &carr[3];
319   else return 0;
320   
321   if (!(*pc))
322   {
323     if (id == IDC_SIZEALL)
324     {
325       static unsigned char p[16*16] = 
326       {
327         0, 0, 0, 0, 0, 0, G, W, W, G, 0, 0, 0, 0, 0, 0,
328         0, 0, 0, 0, 0, G, W, B, B, W, G, 0, 0, 0, 0, 0,
329         0, 0, 0, 0, 0, W, B, B, B, B, W, 0, 0, 0, 0, 0,
330         0, 0, 0, 0, G, B, B, B, B, B, B, G, 0, 0, 0, 0,
331         0, 0, 0, G, 0, 0, W, B, B, W, 0, 0, G, 0, 0, 0,
332         0, G, W, B, 0, 0, W, B, B, W, 0, 0, B, W, G, 0,
333         G, W, B, B, W, W, W, B, B, W, W, W, B, B, W, G,
334         W, B, B, B, B, B, B, B, B, B, B, B, B, B, B, W,
335         W, B, B, B, B, B, B, B, B, B, B, B, B, B, B, W,
336         G, W, B, B, W, W, W, B, B, W, W, W, B, B, W, G,
337         0, G, W, B, 0, 0, W, B, B, W, 0, 0, B, W, G, 0,
338         0, 0, 0, G, 0, 0, W, B, B, W, 0, 0, G, 0, 0, 0,
339         0, 0, 0, 0, G, B, B, B, B, B, B, G, 0, 0, 0, 0,
340         0, 0, 0, 0, 0, W, B, B, B, B, W, 0, 0, 0, 0, 0,
341         0, 0, 0, 0, 0, G, W, B, B, W, G, 0, 0, 0, 0, 0,
342         0, 0, 0, 0, 0, 0, G, W, W, G, 0, 0, 0, 0, 0, 0,
343       };
344       *pc = MakeCursorFromData(p, 8, 8);
345     }
346     else if (id == IDC_SIZENWSE || id == IDC_SIZENESW)
347     {
348       static unsigned char p[16*16] = 
349       {
350         W, W, W, W, W, W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
351         W, G, G, G, W, G, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
352         W, G, B, W, G, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
353         W, G, W, B, W, G, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      
354         W, W, G, W, B, W, G, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
355         W, G, 0, G, W, B, W, G, 0, 0, 0, 0, 0, 0, 0, 0, 
356         0, 0, 0, 0, G, W, B, W, G, 0, 0, 0, 0, 0, 0, 0, 
357         0, 0, 0, 0, 0, G, W, B, W, G, 0, 0, 0, 0, 0, 0,         
358         0, 0, 0, 0, 0, 0, G, W, B, W, G, 0, 0, 0, 0, 0, 
359         0, 0, 0, 0, 0, 0, 0, G, W, B, W, G, 0, 0, 0, 0, 
360         0, 0, 0, 0, 0, 0, 0, 0, G, W, B, W, G, 0, G, W, 
361         0, 0, 0, 0, 0, 0, 0, 0, 0, G, W, B, W, G, W, W, 
362         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, G, W, B, W, G, W, 
363         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, G, W, B, G, W, 
364         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, G, W, G, G, G, W, 
365         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W, W, W, W, W, W,         
366       };
367       if (id == IDC_SIZENESW)
368       {
369         int x, y;
370         for (y = 0; y < 16; ++y) 
371         {
372           for (x = 0; x < 8; ++x)
373           {
374             unsigned char tmp = p[16*y+x];
375             p[16*y+x] = p[16*y+16-x-1];
376             p[16*y+16-x-1] = tmp;
377           }
378         }
379       }
380      *pc = MakeCursorFromData(p, 8, 8);   
381       if (id == IDC_SIZENESW) // swap back!
382       {
383         int x, y;
384         for (y = 0; y < 16; ++y) 
385         {
386           for (x = 0; x < 8; ++x)
387           {
388             unsigned char tmp = p[16*y+x];
389             p[16*y+x] = p[16*y+16-x-1];
390             p[16*y+16-x-1] = tmp;
391           }
392         }
393       }      
394     }
395     else if (id == IDC_NO)
396     {
397       static unsigned char p[16*16] = 
398       {
399         0, 0, 0, 0, G, W, W, W, W, W, W, G, 0, 0, 0, 0,
400         0, 0, G, W, W, B, B, B, B, B, B, W, W, G, 0, 0,
401         0, G, W, B, B, B, W, W, W, W, B, B, B, W, G, 0,
402         0, W, B, B, W, W, G, 0, 0, G, W, G, B, B, W, 0,        
403         G, W, B, W, G, 0, 0, 0, 0, G, W, B, G, B, W, G,
404         W, B, B, W, 0, 0, 0, 0, G, W, B, W, W, B, B, W,
405         W, B, W, G, 0, 0, 0, G, W, B, W, G, G, W, B, W,
406         W, B, W, 0, 0, 0, G, W, B, W, G, 0, 0, W, B, W,      
407         W, B, W, 0, 0, G, W, B, W, G, 0, 0, 0, W, B, W,
408         W, B, W, G, G, W, B, W, G, 0, 0, 0, G, W, B, W,
409         W, B, B, W, W, B, W, G, 0, 0, 0, 0, W, B, B, W,
410         G, W, B, G, B, W, G, 0, 0, 0, 0, G, W, B, W, G,        
411         0, W, B, B, G, W, G, 0, 0, G, W, W, B, B, W, 0,
412         0, G, W, B, B, B, W, W, W, W, B, B, B, W, G, 0,
413         0, 0, G, W, W, B, B, B, B, B, B, W, W, G, 0, 0,
414         0, 0, 0, 0, G, W, W, W, W, W, W, G, 0, 0, 0, 0,                                                                                                                                                                                                                                                                                                                                                                                                                                
415       };
416       *pc = MakeCursorFromData(p, 8, 8);        
417     }
418   }  
419   
420   return *pc;
423 static NSImage *swell_imageFromCursorString(const char *name, POINT *hotSpot)
425   NSImage *img=NULL;
426   FILE *fp = NULL;  
427   bool isFullFn=false;
428   
429   if (!strstr(name,"/") && strlen(name)<1024)
430   {
431     char tmpn[4096];
432     GetModuleFileName(NULL,tmpn,(DWORD)(sizeof(tmpn)-128-strlen(name)));
433     strcat(tmpn,"/Contents/Resources/");
434     strcat(tmpn,name);
435     strcat(tmpn,".cur");
436     fp = fopen(tmpn,"rb");
437   }
438   else 
439   {
440     isFullFn=true;
441     if (strlen(name)>4 && !stricmp(name+strlen(name)-4,".cur")) fp = fopen(name,"rb");    
442   }  
443   
444   if (fp)
445   {
446     unsigned char buf[4096];
447     if (fread(buf,1,6,fp)==6 && !buf[0] && !buf[1] && buf[2] == 2 && buf[3] == 0 && buf[4] == 1 && buf[5] == 0)
448     {
449       static char tempfn[512];
450       if (!tempfn[0])
451       {
452         GetTempPath(256,tempfn);
453         snprintf(tempfn+strlen(tempfn),256,"swellcur%x%x.ico", timeGetTime(),(int)getpid());
454       }
455       
456       FILE *outfp = fopen(tempfn,"wb");
457       if (outfp)
458       {
459         bool wantLoad=false;
460         buf[2]=1; // change to .ico
461         fwrite(buf,1,6,outfp);
462         
463         fread(buf,1,16,fp);
464         int xHot = buf[4]|(buf[5]<<8);
465         int yHot = buf[6]|(buf[7]<<8);
466         
467         buf[4]=1; buf[5]=0; // 1 color plane
468         buf[6]=0; buf[7]=0; // 0 for pixel depth means "auto"
469         
470         if (!buf[3])
471         {
472           fwrite(buf,1,16,outfp);
473           for (;;)
474           {
475             size_t a = fread(buf,1,sizeof(buf),fp);
476             if (a<1) break;
477             fwrite(buf,1,a,outfp);
478           }           
479           wantLoad=true;
480         }              
481         
482         fclose(outfp);
483         if (wantLoad)
484         {
485           NSString *str = (NSString *)SWELL_CStringToCFString(tempfn);     
486           img = [[NSImage alloc] initWithContentsOfFile:str];
487           [str release];
488           if (img && hotSpot) 
489           {
490             hotSpot->x = xHot;
491             hotSpot->y = yHot;
492           }
493           //          printf("loaded converted ico for %s %s %d\n",tempfn,name,!!img);
494         }
495         unlink(tempfn);
496       }      
497       
498     }
499     
500     fclose(fp);
501   }
502   
503   if (!img) // fall back
504   {
505     NSString *str = (NSString *)SWELL_CStringToCFString(name);     
506     
507     if (isFullFn) img = [[NSImage alloc] initWithContentsOfFile:str];
508     else
509     {
510       img = [NSImage imageNamed:str];
511       if (img) [img retain];
512     }
513     [str release];
514   }
515   
516   return img;
519   
520 HCURSOR SWELL_LoadCursorFromFile(const char *fn)
522   POINT hotspot={0,};
523   NSImage *img = swell_imageFromCursorString(fn,&hotspot);    
524   if (img)
525   {      
526     HCURSOR ret=(HCURSOR)[[NSCursor alloc] initWithImage:img hotSpot:NSMakePoint(hotspot.x,hotspot.y)];      
527     [img release];
528     return ret;
529   }
530   return NULL;
532   
533 // todo: support for loading from file
534 HCURSOR SWELL_LoadCursor(const char *_idx)
536   if (_idx == IDC_NO||_idx==IDC_SIZENWSE || _idx == IDC_SIZENESW || _idx == IDC_SIZEALL) return (HCURSOR) MakeSWELLSystemCursor(_idx);
537   if (_idx == IDC_SIZEWE) return (HCURSOR)[NSCursor resizeLeftRightCursor];
538   if (_idx == IDC_SIZENS) return (HCURSOR)[NSCursor resizeUpDownCursor];
539   if (_idx == IDC_ARROW) return (HCURSOR)[NSCursor arrowCursor];
540   if (_idx == IDC_HAND) return (HCURSOR)[NSCursor openHandCursor];
541   if (_idx == IDC_UPARROW) return (HCURSOR)[NSCursor resizeUpCursor];
542   if (_idx == IDC_IBEAM) return (HCURSOR)[NSCursor IBeamCursor];
543   
544   // search registered cursor list
545   SWELL_CursorResourceIndex *p = SWELL_curmodule_cursorresource_head;
546   while (p)
547   {
548     if (p->resid == _idx)
549     {
550       if (p->cachedCursor) return p->cachedCursor;
551       
552       NSImage *img = swell_imageFromCursorString(p->resname,&p->hotspot);    
553       if (img)
554       {      
555         p->cachedCursor=(HCURSOR)[[NSCursor alloc] initWithImage:img hotSpot:NSMakePoint(p->hotspot.x,p->hotspot.y)];      
556         [img release];
557         return p->cachedCursor;
558       }
559     }
560     p=p->_next;
561   }
562   return 0;
565 static HCURSOR m_last_setcursor;
567 void SWELL_SetCursor(HCURSOR curs)
569   if (curs && [(id) curs isKindOfClass:[NSCursor class]])
570   {
571     m_last_setcursor=curs;
572     [(NSCursor *)curs set];
573   }
574   else
575   {
576     m_last_setcursor=NULL;
577     [[NSCursor arrowCursor] set];
578   }
581 HCURSOR SWELL_GetCursor()
583   return (HCURSOR)[NSCursor currentCursor];
585 HCURSOR SWELL_GetLastSetCursor()
587   return m_last_setcursor;
590 static POINT g_swell_mouse_relmode_curpos; // stored in osx-native coordinates (y=0=top of screen)
591 static bool g_swell_mouse_relmode;
595 void GetCursorPos(POINT *pt)
597   if (g_swell_mouse_relmode)
598   {
599     *pt=g_swell_mouse_relmode_curpos;
600     return;
601   }
602   NSPoint localpt=[NSEvent mouseLocation];
603   pt->x=(int)floor(localpt.x);
604   pt->y=-(int)floor(-localpt.y); // floor() is used with negative sign, effectively ceil(), because screen coordinates are flipped and everywhere else we use nonflipped rounding
607 DWORD GetMessagePos()
608 {  
609   if (g_swell_mouse_relmode)
610   {
611     return MAKELONG((int)g_swell_mouse_relmode_curpos.x,(int)g_swell_mouse_relmode_curpos.y);
612   }
613   NSPoint localpt=[NSEvent mouseLocation];
614   return MAKELONG((int)floor(localpt.x), -(int)floor(-localpt.y)); // floor() is used with negative sign, effectively ceil(), because screen coordinates are flipped and everywhere else we use nonflipped rounding
618 NSPoint swellProcessMouseEvent(int msg, NSView *view, NSEvent *event)
620   if (g_swell_mouse_relmode && msg==WM_MOUSEMOVE) // event will have relative coordinates
621   {
622     int idx=(int)[event deltaX];
623     int idy=(int)[event deltaY];
624     g_swell_mouse_relmode_curpos.x += idx;
625     g_swell_mouse_relmode_curpos.y -= idy;
626   }
627   if (g_swell_mouse_relmode) 
628   {
629     POINT p=g_swell_mouse_relmode_curpos;
630     ScreenToClient((HWND)view,&p);
631     return NSMakePoint(p.x,p.y);
632   }
633   NSPoint localpt=[event locationInWindow];
634   return [view convertPoint:localpt fromView:nil];
637 static int m_curvis_cnt;
638 bool SWELL_IsCursorVisible()
640   return m_curvis_cnt>=0;
642 int SWELL_ShowCursor(BOOL bShow)
644   m_curvis_cnt += (bShow?1:-1);
645   if (m_curvis_cnt==-1 && !bShow) 
646   {
647     GetCursorPos(&g_swell_mouse_relmode_curpos);
648     CGDisplayHideCursor(kCGDirectMainDisplay);
649     CGAssociateMouseAndMouseCursorPosition(false);
650     g_swell_mouse_relmode=true;
651   }
652   if (m_curvis_cnt==0 && bShow) 
653   {
654     CGDisplayShowCursor(kCGDirectMainDisplay);
655     CGAssociateMouseAndMouseCursorPosition(true);
656     g_swell_mouse_relmode=false;
657     SetCursorPos(g_swell_mouse_relmode_curpos.x,g_swell_mouse_relmode_curpos.y);
658   }  
659   return m_curvis_cnt;
663 BOOL SWELL_SetCursorPos(int X, int Y)
664 {  
665   if (g_swell_mouse_relmode)
666   {
667     g_swell_mouse_relmode_curpos.x=X;
668     g_swell_mouse_relmode_curpos.y=Y;
669     return TRUE;
670   }
672   const int h = (int)CGDisplayPixelsHigh(CGMainDisplayID());
673   CGPoint pos=CGPointMake(X,h-Y);
674   return CGWarpMouseCursorPosition(pos)==kCGErrorSuccess;
677 void SWELL_Register_Cursor_Resource(const char *idx, const char *name, int hotspot_x, int hotspot_y)
679   SWELL_CursorResourceIndex *ri = (SWELL_CursorResourceIndex*)malloc(sizeof(SWELL_CursorResourceIndex));
680   ri->hotspot.x = hotspot_x;
681   ri->hotspot.y = hotspot_y;
682   ri->resname=name;
683   ri->cachedCursor=0;
684   ri->resid = idx;
685   ri->_next = SWELL_curmodule_cursorresource_head;
686   SWELL_curmodule_cursorresource_head = ri;
692 #endif