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.
21 This file provides basic key and mouse cursor querying, as well as a mac key to windows key translation function.
25 #ifndef SWELL_PROVIDED_BY_APP
28 #include "swell-dlggen.h"
29 #import <Cocoa/Cocoa.h>
30 #import <Carbon/Carbon.h>
34 static int MacKeyCodeToVK(int code)
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;
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
95 static TISInputSourceRef (*_TISCopyCurrentKeyboardInputSource)( void);
96 static void* (*_TISGetInputSourceProperty) ( TISInputSourceRef inputSource, CFStringRef propertyKey);
101 CFBundleRef b = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon"));
104 *(void **)&_TISGetInputSourceProperty = CFBundleGetFunctionPointerForName(b,CFSTR("TISGetInputSourceProperty"));
105 *(void **)&_TISCopyCurrentKeyboardInputSource = CFBundleGetFunctionPointerForName(b,CFSTR("TISCopyCurrentKeyboardInputSource"));
108 if (!_TISCopyCurrentKeyboardInputSource || !_TISGetInputSourceProperty) return 0;
110 TISInputSourceRef currentKeyboard = _TISCopyCurrentKeyboardInputSource();
111 CFDataRef uchr = (CFDataRef)_TISGetInputSourceProperty(currentKeyboard, CFSTR("TISPropertyUnicodeKeyLayoutData"));
112 const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
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,
126 &actualStringLength, unicodeString);
128 if (actualStringLength == 0 && deadKeyState)
130 status = UCKeyTranslate(keyboardLayout,
131 kVK_Space, kUCKeyActionDown, 0,
135 &actualStringLength, unicodeString);
137 if(actualStringLength > 0 && status == noErr) return unicodeString[0];
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];
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;
156 int rawcode=[theEvent keyCode];
158 int code=MacKeyCodeToVK(rawcode);
162 if (mode == 1) str=[theEvent characters];
164 if (!str || ![str length]) str=[theEvent charactersIgnoringModifiers];
166 if (!str || ![str length])
168 #ifdef MAC_OS_X_VERSION_10_5
169 if (mode==1) code=charFromVcode(rawcode);
173 code = 1024+rawcode; // raw code
179 code=[str characterAtIndex:0];
180 if (code >= NSF1FunctionKey && code <= NSF24FunctionKey)
183 code += VK_F1 - NSF1FunctionKey;
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;
196 if (code==8) code='\b';
199 if (!(flag&FVIRTKEY)) flag&=~FSHIFT;
201 if (flags) *flags=flag;
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))
215 *newflags = lParam&~(FSHIFT|FVIRTKEY);
216 if (!(lParam & (FCONTROL|FLWIN))) switch (wParam)
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 ')';
234 WORD GetAsyncKeyState(int key)
236 CGEventFlags state=0;
237 if (key == VK_LBUTTON || key == VK_RBUTTON || key == VK_MBUTTON)
239 state=GetCurrentEventButtonState();
243 state=CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState);
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)))
261 static SWELL_CursorResourceIndex *SWELL_curmodule_cursorresource_head;
263 static NSCursor* MakeCursorFromData(unsigned char* data, int hotspot_x, int hotspot_y)
266 NSBitmapImageRep* bmp = [[NSBitmapImageRep alloc]
267 initWithBitmapDataPlanes:0
274 colorSpaceName:NSCalibratedWhiteColorSpace
280 unsigned char* p = [bmp bitmapData];
284 for (i = 0; i < 16*16; ++i)
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);
291 NSImage *img = [[NSImage alloc] init];
294 [img addRepresentation:bmp];
295 NSPoint hs = NSMakePoint(hotspot_x, hotspot_y);
296 c = [[NSCursor alloc] initWithImage:img hotSpot:hs];
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;
312 static NSCursor* carr[4] = { 0, 0, 0, 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];
323 if (id == IDC_SIZEALL)
325 static unsigned char p[16*16] =
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,
344 *pc = MakeCursorFromData(p, 8, 8);
346 else if (id == IDC_SIZENWSE || id == IDC_SIZENESW)
348 static unsigned char p[16*16] =
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,
367 if (id == IDC_SIZENESW)
370 for (y = 0; y < 16; ++y)
372 for (x = 0; x < 8; ++x)
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;
380 *pc = MakeCursorFromData(p, 8, 8);
381 if (id == IDC_SIZENESW) // swap back!
384 for (y = 0; y < 16; ++y)
386 for (x = 0; x < 8; ++x)
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;
395 else if (id == IDC_NO)
397 static unsigned char p[16*16] =
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,
416 *pc = MakeCursorFromData(p, 8, 8);
423 static NSImage *swell_imageFromCursorString(const char *name, POINT *hotSpot)
429 if (!strstr(name,"/") && strlen(name)<1024)
432 GetModuleFileName(NULL,tmpn,(DWORD)(sizeof(tmpn)-128-strlen(name)));
433 strcat(tmpn,"/Contents/Resources/");
436 fp = fopen(tmpn,"rb");
441 if (strlen(name)>4 && !stricmp(name+strlen(name)-4,".cur")) fp = fopen(name,"rb");
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)
449 static char tempfn[512];
452 GetTempPath(256,tempfn);
453 snprintf(tempfn+strlen(tempfn),256,"swellcur%x%x.ico", timeGetTime(),(int)getpid());
456 FILE *outfp = fopen(tempfn,"wb");
460 buf[2]=1; // change to .ico
461 fwrite(buf,1,6,outfp);
464 int xHot = buf[4]|(buf[5]<<8);
465 int yHot = buf[6]|(buf[7]<<8);
467 buf[4]=1; buf[5]=0; // 1 color plane
468 buf[6]=0; buf[7]=0; // 0 for pixel depth means "auto"
472 fwrite(buf,1,16,outfp);
475 size_t a = fread(buf,1,sizeof(buf),fp);
477 fwrite(buf,1,a,outfp);
485 NSString *str = (NSString *)SWELL_CStringToCFString(tempfn);
486 img = [[NSImage alloc] initWithContentsOfFile:str];
493 // printf("loaded converted ico for %s %s %d\n",tempfn,name,!!img);
503 if (!img) // fall back
505 NSString *str = (NSString *)SWELL_CStringToCFString(name);
507 if (isFullFn) img = [[NSImage alloc] initWithContentsOfFile:str];
510 img = [NSImage imageNamed:str];
511 if (img) [img retain];
520 HCURSOR SWELL_LoadCursorFromFile(const char *fn)
523 NSImage *img = swell_imageFromCursorString(fn,&hotspot);
526 HCURSOR ret=(HCURSOR)[[NSCursor alloc] initWithImage:img hotSpot:NSMakePoint(hotspot.x,hotspot.y)];
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];
544 // search registered cursor list
545 SWELL_CursorResourceIndex *p = SWELL_curmodule_cursorresource_head;
548 if (p->resid == _idx)
550 if (p->cachedCursor) return p->cachedCursor;
552 NSImage *img = swell_imageFromCursorString(p->resname,&p->hotspot);
555 p->cachedCursor=(HCURSOR)[[NSCursor alloc] initWithImage:img hotSpot:NSMakePoint(p->hotspot.x,p->hotspot.y)];
557 return p->cachedCursor;
565 static HCURSOR m_last_setcursor;
567 void SWELL_SetCursor(HCURSOR curs)
569 if (curs && [(id) curs isKindOfClass:[NSCursor class]])
571 m_last_setcursor=curs;
572 [(NSCursor *)curs set];
576 m_last_setcursor=NULL;
577 [[NSCursor arrowCursor] set];
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)
599 *pt=g_swell_mouse_relmode_curpos;
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()
609 if (g_swell_mouse_relmode)
611 return MAKELONG((int)g_swell_mouse_relmode_curpos.x,(int)g_swell_mouse_relmode_curpos.y);
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
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;
627 if (g_swell_mouse_relmode)
629 POINT p=g_swell_mouse_relmode_curpos;
630 ScreenToClient((HWND)view,&p);
631 return NSMakePoint(p.x,p.y);
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)
647 GetCursorPos(&g_swell_mouse_relmode_curpos);
648 CGDisplayHideCursor(kCGDirectMainDisplay);
649 CGAssociateMouseAndMouseCursorPosition(false);
650 g_swell_mouse_relmode=true;
652 if (m_curvis_cnt==0 && bShow)
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);
663 BOOL SWELL_SetCursorPos(int X, int Y)
665 if (g_swell_mouse_relmode)
667 g_swell_mouse_relmode_curpos.x=X;
668 g_swell_mouse_relmode_curpos.y=Y;
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;
685 ri->_next = SWELL_curmodule_cursorresource_head;
686 SWELL_curmodule_cursorresource_head = ri;