cocoa.m 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. /*
  2. * QEMU Cocoa CG display driver
  3. *
  4. * Copyright (c) 2008 Mike Kronenberg
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. #import <Cocoa/Cocoa.h>
  25. #include <crt_externs.h>
  26. #include "qemu-common.h"
  27. #include "console.h"
  28. #include "sysemu.h"
  29. #ifndef MAC_OS_X_VERSION_10_4
  30. #define MAC_OS_X_VERSION_10_4 1040
  31. #endif
  32. #ifndef MAC_OS_X_VERSION_10_5
  33. #define MAC_OS_X_VERSION_10_5 1050
  34. #endif
  35. //#define DEBUG
  36. #ifdef DEBUG
  37. #define COCOA_DEBUG(...) { (void) fprintf (stdout, __VA_ARGS__); }
  38. #else
  39. #define COCOA_DEBUG(...) ((void) 0)
  40. #endif
  41. #define cgrect(nsrect) (*(CGRect *)&(nsrect))
  42. #define COCOA_MOUSE_EVENT \
  43. if (isTabletEnabled) { \
  44. kbd_mouse_event((int)(p.x * 0x7FFF / (screen.width - 1)), (int)((screen.height - p.y) * 0x7FFF / (screen.height - 1)), 0, buttons); \
  45. } else if (isMouseGrabed) { \
  46. kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \
  47. } else { \
  48. [NSApp sendEvent:event]; \
  49. }
  50. typedef struct {
  51. int width;
  52. int height;
  53. int bitsPerComponent;
  54. int bitsPerPixel;
  55. } QEMUScreen;
  56. NSWindow *normalWindow;
  57. static DisplayChangeListener *dcl;
  58. int gArgc;
  59. char **gArgv;
  60. // keymap conversion
  61. int keymap[] =
  62. {
  63. // SdlI macI macH SdlH 104xtH 104xtC sdl
  64. 30, // 0 0x00 0x1e A QZ_a
  65. 31, // 1 0x01 0x1f S QZ_s
  66. 32, // 2 0x02 0x20 D QZ_d
  67. 33, // 3 0x03 0x21 F QZ_f
  68. 35, // 4 0x04 0x23 H QZ_h
  69. 34, // 5 0x05 0x22 G QZ_g
  70. 44, // 6 0x06 0x2c Z QZ_z
  71. 45, // 7 0x07 0x2d X QZ_x
  72. 46, // 8 0x08 0x2e C QZ_c
  73. 47, // 9 0x09 0x2f V QZ_v
  74. 0, // 10 0x0A Undefined
  75. 48, // 11 0x0B 0x30 B QZ_b
  76. 16, // 12 0x0C 0x10 Q QZ_q
  77. 17, // 13 0x0D 0x11 W QZ_w
  78. 18, // 14 0x0E 0x12 E QZ_e
  79. 19, // 15 0x0F 0x13 R QZ_r
  80. 21, // 16 0x10 0x15 Y QZ_y
  81. 20, // 17 0x11 0x14 T QZ_t
  82. 2, // 18 0x12 0x02 1 QZ_1
  83. 3, // 19 0x13 0x03 2 QZ_2
  84. 4, // 20 0x14 0x04 3 QZ_3
  85. 5, // 21 0x15 0x05 4 QZ_4
  86. 7, // 22 0x16 0x07 6 QZ_6
  87. 6, // 23 0x17 0x06 5 QZ_5
  88. 13, // 24 0x18 0x0d = QZ_EQUALS
  89. 10, // 25 0x19 0x0a 9 QZ_9
  90. 8, // 26 0x1A 0x08 7 QZ_7
  91. 12, // 27 0x1B 0x0c - QZ_MINUS
  92. 9, // 28 0x1C 0x09 8 QZ_8
  93. 11, // 29 0x1D 0x0b 0 QZ_0
  94. 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET
  95. 24, // 31 0x1F 0x18 O QZ_o
  96. 22, // 32 0x20 0x16 U QZ_u
  97. 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET
  98. 23, // 34 0x22 0x17 I QZ_i
  99. 25, // 35 0x23 0x19 P QZ_p
  100. 28, // 36 0x24 0x1c ENTER QZ_RETURN
  101. 38, // 37 0x25 0x26 L QZ_l
  102. 36, // 38 0x26 0x24 J QZ_j
  103. 40, // 39 0x27 0x28 ' QZ_QUOTE
  104. 37, // 40 0x28 0x25 K QZ_k
  105. 39, // 41 0x29 0x27 ; QZ_SEMICOLON
  106. 43, // 42 0x2A 0x2b \ QZ_BACKSLASH
  107. 51, // 43 0x2B 0x33 , QZ_COMMA
  108. 53, // 44 0x2C 0x35 / QZ_SLASH
  109. 49, // 45 0x2D 0x31 N QZ_n
  110. 50, // 46 0x2E 0x32 M QZ_m
  111. 52, // 47 0x2F 0x34 . QZ_PERIOD
  112. 15, // 48 0x30 0x0f TAB QZ_TAB
  113. 57, // 49 0x31 0x39 SPACE QZ_SPACE
  114. 41, // 50 0x32 0x29 ` QZ_BACKQUOTE
  115. 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE
  116. 0, // 52 0x34 Undefined
  117. 1, // 53 0x35 0x01 ESC QZ_ESCAPE
  118. 0, // 54 0x36 QZ_RMETA
  119. 0, // 55 0x37 QZ_LMETA
  120. 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT
  121. 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK
  122. 56, // 58 0x3A 0x38 L ALT QZ_LALT
  123. 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL
  124. 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT
  125. 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT
  126. 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL
  127. 0, // 63 0x3F Undefined
  128. 0, // 64 0x40 Undefined
  129. 0, // 65 0x41 Undefined
  130. 0, // 66 0x42 Undefined
  131. 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY
  132. 0, // 68 0x44 Undefined
  133. 78, // 69 0x45 0x4e KP + QZ_KP_PLUS
  134. 0, // 70 0x46 Undefined
  135. 69, // 71 0x47 0x45 NUM QZ_NUMLOCK
  136. 0, // 72 0x48 Undefined
  137. 0, // 73 0x49 Undefined
  138. 0, // 74 0x4A Undefined
  139. 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE
  140. 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER
  141. 0, // 77 0x4D undefined
  142. 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS
  143. 0, // 79 0x4F Undefined
  144. 0, // 80 0x50 Undefined
  145. 0, // 81 0x51 QZ_KP_EQUALS
  146. 82, // 82 0x52 0x52 KP 0 QZ_KP0
  147. 79, // 83 0x53 0x4f KP 1 QZ_KP1
  148. 80, // 84 0x54 0x50 KP 2 QZ_KP2
  149. 81, // 85 0x55 0x51 KP 3 QZ_KP3
  150. 75, // 86 0x56 0x4b KP 4 QZ_KP4
  151. 76, // 87 0x57 0x4c KP 5 QZ_KP5
  152. 77, // 88 0x58 0x4d KP 6 QZ_KP6
  153. 71, // 89 0x59 0x47 KP 7 QZ_KP7
  154. 0, // 90 0x5A Undefined
  155. 72, // 91 0x5B 0x48 KP 8 QZ_KP8
  156. 73, // 92 0x5C 0x49 KP 9 QZ_KP9
  157. 0, // 93 0x5D Undefined
  158. 0, // 94 0x5E Undefined
  159. 0, // 95 0x5F Undefined
  160. 63, // 96 0x60 0x3f F5 QZ_F5
  161. 64, // 97 0x61 0x40 F6 QZ_F6
  162. 65, // 98 0x62 0x41 F7 QZ_F7
  163. 61, // 99 0x63 0x3d F3 QZ_F3
  164. 66, // 100 0x64 0x42 F8 QZ_F8
  165. 67, // 101 0x65 0x43 F9 QZ_F9
  166. 0, // 102 0x66 Undefined
  167. 87, // 103 0x67 0x57 F11 QZ_F11
  168. 0, // 104 0x68 Undefined
  169. 183,// 105 0x69 0xb7 QZ_PRINT
  170. 0, // 106 0x6A Undefined
  171. 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK
  172. 0, // 108 0x6C Undefined
  173. 68, // 109 0x6D 0x44 F10 QZ_F10
  174. 0, // 110 0x6E Undefined
  175. 88, // 111 0x6F 0x58 F12 QZ_F12
  176. 0, // 112 0x70 Undefined
  177. 110,// 113 0x71 0x0 QZ_PAUSE
  178. 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT
  179. 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME
  180. 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP
  181. 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE
  182. 62, // 118 0x76 0x3e F4 QZ_F4
  183. 207,// 119 0x77 0xcf E0,4f END QZ_END
  184. 60, // 120 0x78 0x3c F2 QZ_F2
  185. 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN
  186. 59, // 122 0x7A 0x3b F1 QZ_F1
  187. 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT
  188. 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT
  189. 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN
  190. 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP
  191. /* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
  192. /* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
  193. /*
  194. 219 // 0xdb e0,5b L GUI
  195. 220 // 0xdc e0,5c R GUI
  196. 221 // 0xdd e0,5d APPS
  197. // E0,2A,E0,37 PRNT SCRN
  198. // E1,1D,45,E1,9D,C5 PAUSE
  199. 83 // 0x53 0x53 KP .
  200. // ACPI Scan Codes
  201. 222 // 0xde E0, 5E Power
  202. 223 // 0xdf E0, 5F Sleep
  203. 227 // 0xe3 E0, 63 Wake
  204. // Windows Multimedia Scan Codes
  205. 153 // 0x99 E0, 19 Next Track
  206. 144 // 0x90 E0, 10 Previous Track
  207. 164 // 0xa4 E0, 24 Stop
  208. 162 // 0xa2 E0, 22 Play/Pause
  209. 160 // 0xa0 E0, 20 Mute
  210. 176 // 0xb0 E0, 30 Volume Up
  211. 174 // 0xae E0, 2E Volume Down
  212. 237 // 0xed E0, 6D Media Select
  213. 236 // 0xec E0, 6C E-Mail
  214. 161 // 0xa1 E0, 21 Calculator
  215. 235 // 0xeb E0, 6B My Computer
  216. 229 // 0xe5 E0, 65 WWW Search
  217. 178 // 0xb2 E0, 32 WWW Home
  218. 234 // 0xea E0, 6A WWW Back
  219. 233 // 0xe9 E0, 69 WWW Forward
  220. 232 // 0xe8 E0, 68 WWW Stop
  221. 231 // 0xe7 E0, 67 WWW Refresh
  222. 230 // 0xe6 E0, 66 WWW Favorites
  223. */
  224. };
  225. static int cocoa_keycode_to_qemu(int keycode)
  226. {
  227. if((sizeof(keymap)/sizeof(int)) <= keycode)
  228. {
  229. printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
  230. return 0;
  231. }
  232. return keymap[keycode];
  233. }
  234. /*
  235. ------------------------------------------------------
  236. QemuCocoaView
  237. ------------------------------------------------------
  238. */
  239. @interface QemuCocoaView : NSView
  240. {
  241. QEMUScreen screen;
  242. NSWindow *fullScreenWindow;
  243. float cx,cy,cw,ch,cdx,cdy;
  244. CGDataProviderRef dataProviderRef;
  245. int modifiers_state[256];
  246. BOOL isMouseGrabed;
  247. BOOL isFullscreen;
  248. BOOL isAbsoluteEnabled;
  249. BOOL isTabletEnabled;
  250. }
  251. - (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds;
  252. - (void) grabMouse;
  253. - (void) ungrabMouse;
  254. - (void) toggleFullScreen:(id)sender;
  255. - (void) handleEvent:(NSEvent *)event;
  256. - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;
  257. - (BOOL) isMouseGrabed;
  258. - (BOOL) isAbsoluteEnabled;
  259. - (float) cdx;
  260. - (float) cdy;
  261. - (QEMUScreen) gscreen;
  262. @end
  263. QemuCocoaView *cocoaView;
  264. @implementation QemuCocoaView
  265. - (id)initWithFrame:(NSRect)frameRect
  266. {
  267. COCOA_DEBUG("QemuCocoaView: initWithFrame\n");
  268. self = [super initWithFrame:frameRect];
  269. if (self) {
  270. screen.bitsPerComponent = 8;
  271. screen.bitsPerPixel = 32;
  272. screen.width = frameRect.size.width;
  273. screen.height = frameRect.size.height;
  274. }
  275. return self;
  276. }
  277. - (void) dealloc
  278. {
  279. COCOA_DEBUG("QemuCocoaView: dealloc\n");
  280. if (dataProviderRef)
  281. CGDataProviderRelease(dataProviderRef);
  282. [super dealloc];
  283. }
  284. - (BOOL) isOpaque
  285. {
  286. return YES;
  287. }
  288. - (void) drawRect:(NSRect) rect
  289. {
  290. COCOA_DEBUG("QemuCocoaView: drawRect\n");
  291. // get CoreGraphic context
  292. CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort];
  293. CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
  294. CGContextSetShouldAntialias (viewContextRef, NO);
  295. // draw screen bitmap directly to Core Graphics context
  296. if (dataProviderRef) {
  297. CGImageRef imageRef = CGImageCreate(
  298. screen.width, //width
  299. screen.height, //height
  300. screen.bitsPerComponent, //bitsPerComponent
  301. screen.bitsPerPixel, //bitsPerPixel
  302. (screen.width * (screen.bitsPerComponent/2)), //bytesPerRow
  303. #ifdef __LITTLE_ENDIAN__
  304. CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace for OS X >= 10.4
  305. kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
  306. #else
  307. CGColorSpaceCreateDeviceRGB(), //colorspace for OS X < 10.4 (actually ppc)
  308. kCGImageAlphaNoneSkipFirst, //bitmapInfo
  309. #endif
  310. dataProviderRef, //provider
  311. NULL, //decode
  312. 0, //interpolate
  313. kCGRenderingIntentDefault //intent
  314. );
  315. // test if host supports "CGImageCreateWithImageInRect" at compile time
  316. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
  317. if (CGImageCreateWithImageInRect == NULL) { // test if "CGImageCreateWithImageInRect" is supported on host at runtime
  318. #endif
  319. // compatibility drawing code (draws everything) (OS X < 10.4)
  320. CGContextDrawImage (viewContextRef, CGRectMake(0, 0, [self bounds].size.width, [self bounds].size.height), imageRef);
  321. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
  322. } else {
  323. // selective drawing code (draws only dirty rectangles) (OS X >= 10.4)
  324. const NSRect *rectList;
  325. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
  326. NSInteger rectCount;
  327. #else
  328. int rectCount;
  329. #endif
  330. int i;
  331. CGImageRef clipImageRef;
  332. CGRect clipRect;
  333. [self getRectsBeingDrawn:&rectList count:&rectCount];
  334. for (i = 0; i < rectCount; i++) {
  335. clipRect.origin.x = rectList[i].origin.x / cdx;
  336. clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy;
  337. clipRect.size.width = rectList[i].size.width / cdx;
  338. clipRect.size.height = rectList[i].size.height / cdy;
  339. clipImageRef = CGImageCreateWithImageInRect(
  340. imageRef,
  341. clipRect
  342. );
  343. CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
  344. CGImageRelease (clipImageRef);
  345. }
  346. }
  347. #endif
  348. CGImageRelease (imageRef);
  349. }
  350. }
  351. - (void) setContentDimensions
  352. {
  353. COCOA_DEBUG("QemuCocoaView: setContentDimensions\n");
  354. if (isFullscreen) {
  355. cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width;
  356. cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height;
  357. cw = screen.width * cdx;
  358. ch = screen.height * cdy;
  359. cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0;
  360. cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0;
  361. } else {
  362. cx = 0;
  363. cy = 0;
  364. cw = screen.width;
  365. ch = screen.height;
  366. cdx = 1.0;
  367. cdy = 1.0;
  368. }
  369. }
  370. - (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds
  371. {
  372. COCOA_DEBUG("QemuCocoaView: resizeContent\n");
  373. // update screenBuffer
  374. if (dataProviderRef)
  375. CGDataProviderRelease(dataProviderRef);
  376. //sync host window color space with guests
  377. screen.bitsPerPixel = ds_get_bits_per_pixel(ds);
  378. screen.bitsPerComponent = ds_get_bytes_per_pixel(ds) * 2;
  379. dataProviderRef = CGDataProviderCreateWithData(NULL, ds_get_data(ds), w * 4 * h, NULL);
  380. // update windows
  381. if (isFullscreen) {
  382. [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]];
  383. [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:NO animate:NO];
  384. } else {
  385. if (qemu_name)
  386. [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
  387. [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:YES animate:NO];
  388. }
  389. screen.width = w;
  390. screen.height = h;
  391. [normalWindow center];
  392. [self setContentDimensions];
  393. [self setFrame:NSMakeRect(cx, cy, cw, ch)];
  394. }
  395. - (void) toggleFullScreen:(id)sender
  396. {
  397. COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n");
  398. if (isFullscreen) { // switch from fullscreen to desktop
  399. isFullscreen = FALSE;
  400. [self ungrabMouse];
  401. [self setContentDimensions];
  402. // test if host supports "exitFullScreenModeWithOptions" at compile time
  403. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
  404. if ([NSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)]) { // test if "exitFullScreenModeWithOptions" is supported on host at runtime
  405. [self exitFullScreenModeWithOptions:nil];
  406. } else {
  407. #endif
  408. [fullScreenWindow close];
  409. [normalWindow setContentView: self];
  410. [normalWindow makeKeyAndOrderFront: self];
  411. [NSMenu setMenuBarVisible:YES];
  412. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
  413. }
  414. #endif
  415. } else { // switch from desktop to fullscreen
  416. isFullscreen = TRUE;
  417. [self grabMouse];
  418. [self setContentDimensions];
  419. // test if host supports "enterFullScreenMode:withOptions" at compile time
  420. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
  421. if ([NSView respondsToSelector:@selector(enterFullScreenMode:withOptions:)]) { // test if "enterFullScreenMode:withOptions" is supported on host at runtime
  422. [self enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys:
  423. [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens,
  424. [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kCGDisplayModeIsStretched, nil], NSFullScreenModeSetting,
  425. nil]];
  426. } else {
  427. #endif
  428. [NSMenu setMenuBarVisible:NO];
  429. fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame]
  430. styleMask:NSBorderlessWindowMask
  431. backing:NSBackingStoreBuffered
  432. defer:NO];
  433. [fullScreenWindow setHasShadow:NO];
  434. [fullScreenWindow setContentView:self];
  435. [fullScreenWindow makeKeyAndOrderFront:self];
  436. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
  437. }
  438. #endif
  439. }
  440. }
  441. - (void) handleEvent:(NSEvent *)event
  442. {
  443. COCOA_DEBUG("QemuCocoaView: handleEvent\n");
  444. int buttons = 0;
  445. int keycode;
  446. NSPoint p = [event locationInWindow];
  447. switch ([event type]) {
  448. case NSFlagsChanged:
  449. keycode = cocoa_keycode_to_qemu([event keyCode]);
  450. if (keycode) {
  451. if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup
  452. kbd_put_keycode(keycode);
  453. kbd_put_keycode(keycode | 0x80);
  454. } else if (is_graphic_console()) {
  455. if (keycode & 0x80)
  456. kbd_put_keycode(0xe0);
  457. if (modifiers_state[keycode] == 0) { // keydown
  458. kbd_put_keycode(keycode & 0x7f);
  459. modifiers_state[keycode] = 1;
  460. } else { // keyup
  461. kbd_put_keycode(keycode | 0x80);
  462. modifiers_state[keycode] = 0;
  463. }
  464. }
  465. }
  466. // release Mouse grab when pressing ctrl+alt
  467. if (!isFullscreen && ([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
  468. [self ungrabMouse];
  469. }
  470. break;
  471. case NSKeyDown:
  472. // forward command Key Combos
  473. if ([event modifierFlags] & NSCommandKeyMask) {
  474. [NSApp sendEvent:event];
  475. return;
  476. }
  477. // default
  478. keycode = cocoa_keycode_to_qemu([event keyCode]);
  479. // handle control + alt Key Combos (ctrl+alt is reserved for QEMU)
  480. if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
  481. switch (keycode) {
  482. // enable graphic console
  483. case 0x02 ... 0x0a: // '1' to '9' keys
  484. console_select(keycode - 0x02);
  485. break;
  486. }
  487. // handle keys for graphic console
  488. } else if (is_graphic_console()) {
  489. if (keycode & 0x80) //check bit for e0 in front
  490. kbd_put_keycode(0xe0);
  491. kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front
  492. // handlekeys for Monitor
  493. } else {
  494. int keysym = 0;
  495. switch([event keyCode]) {
  496. case 115:
  497. keysym = QEMU_KEY_HOME;
  498. break;
  499. case 117:
  500. keysym = QEMU_KEY_DELETE;
  501. break;
  502. case 119:
  503. keysym = QEMU_KEY_END;
  504. break;
  505. case 123:
  506. keysym = QEMU_KEY_LEFT;
  507. break;
  508. case 124:
  509. keysym = QEMU_KEY_RIGHT;
  510. break;
  511. case 125:
  512. keysym = QEMU_KEY_DOWN;
  513. break;
  514. case 126:
  515. keysym = QEMU_KEY_UP;
  516. break;
  517. default:
  518. {
  519. NSString *ks = [event characters];
  520. if ([ks length] > 0)
  521. keysym = [ks characterAtIndex:0];
  522. }
  523. }
  524. if (keysym)
  525. kbd_put_keysym(keysym);
  526. }
  527. break;
  528. case NSKeyUp:
  529. keycode = cocoa_keycode_to_qemu([event keyCode]);
  530. if (is_graphic_console()) {
  531. if (keycode & 0x80)
  532. kbd_put_keycode(0xe0);
  533. kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
  534. }
  535. break;
  536. case NSMouseMoved:
  537. if (isAbsoluteEnabled) {
  538. if (p.x < 0 || p.x > screen.width || p.y < 0 || p.y > screen.height || ![[self window] isKeyWindow]) {
  539. if (isTabletEnabled) { // if we leave the window, deactivate the tablet
  540. [NSCursor unhide];
  541. isTabletEnabled = FALSE;
  542. }
  543. } else {
  544. if (!isTabletEnabled) { // if we enter the window, activate the tablet
  545. [NSCursor hide];
  546. isTabletEnabled = TRUE;
  547. }
  548. }
  549. }
  550. COCOA_MOUSE_EVENT
  551. break;
  552. case NSLeftMouseDown:
  553. if ([event modifierFlags] & NSCommandKeyMask) {
  554. buttons |= MOUSE_EVENT_RBUTTON;
  555. } else {
  556. buttons |= MOUSE_EVENT_LBUTTON;
  557. }
  558. COCOA_MOUSE_EVENT
  559. break;
  560. case NSRightMouseDown:
  561. buttons |= MOUSE_EVENT_RBUTTON;
  562. COCOA_MOUSE_EVENT
  563. break;
  564. case NSOtherMouseDown:
  565. buttons |= MOUSE_EVENT_MBUTTON;
  566. COCOA_MOUSE_EVENT
  567. break;
  568. case NSLeftMouseDragged:
  569. if ([event modifierFlags] & NSCommandKeyMask) {
  570. buttons |= MOUSE_EVENT_RBUTTON;
  571. } else {
  572. buttons |= MOUSE_EVENT_LBUTTON;
  573. }
  574. COCOA_MOUSE_EVENT
  575. break;
  576. case NSRightMouseDragged:
  577. buttons |= MOUSE_EVENT_RBUTTON;
  578. COCOA_MOUSE_EVENT
  579. break;
  580. case NSOtherMouseDragged:
  581. buttons |= MOUSE_EVENT_MBUTTON;
  582. COCOA_MOUSE_EVENT
  583. break;
  584. case NSLeftMouseUp:
  585. if (isTabletEnabled) {
  586. COCOA_MOUSE_EVENT
  587. } else if (!isMouseGrabed) {
  588. if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) {
  589. [self grabMouse];
  590. } else {
  591. [NSApp sendEvent:event];
  592. }
  593. } else {
  594. COCOA_MOUSE_EVENT
  595. }
  596. break;
  597. case NSRightMouseUp:
  598. COCOA_MOUSE_EVENT
  599. break;
  600. case NSOtherMouseUp:
  601. COCOA_MOUSE_EVENT
  602. break;
  603. case NSScrollWheel:
  604. if (isTabletEnabled || isMouseGrabed) {
  605. kbd_mouse_event(0, 0, -[event deltaY], 0);
  606. } else {
  607. [NSApp sendEvent:event];
  608. }
  609. break;
  610. default:
  611. [NSApp sendEvent:event];
  612. }
  613. }
  614. - (void) grabMouse
  615. {
  616. COCOA_DEBUG("QemuCocoaView: grabMouse\n");
  617. if (!isFullscreen) {
  618. if (qemu_name)
  619. [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt to release Mouse)", qemu_name]];
  620. else
  621. [normalWindow setTitle:@"QEMU - (Press ctrl + alt to release Mouse)"];
  622. }
  623. [NSCursor hide];
  624. CGAssociateMouseAndMouseCursorPosition(FALSE);
  625. isMouseGrabed = TRUE; // while isMouseGrabed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:]
  626. }
  627. - (void) ungrabMouse
  628. {
  629. COCOA_DEBUG("QemuCocoaView: ungrabMouse\n");
  630. if (!isFullscreen) {
  631. if (qemu_name)
  632. [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
  633. else
  634. [normalWindow setTitle:@"QEMU"];
  635. }
  636. [NSCursor unhide];
  637. CGAssociateMouseAndMouseCursorPosition(TRUE);
  638. isMouseGrabed = FALSE;
  639. }
  640. - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;}
  641. - (BOOL) isMouseGrabed {return isMouseGrabed;}
  642. - (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;}
  643. - (float) cdx {return cdx;}
  644. - (float) cdy {return cdy;}
  645. - (QEMUScreen) gscreen {return screen;}
  646. @end
  647. /*
  648. ------------------------------------------------------
  649. QemuCocoaAppController
  650. ------------------------------------------------------
  651. */
  652. @interface QemuCocoaAppController : NSObject
  653. {
  654. }
  655. - (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
  656. - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
  657. - (void)toggleFullScreen:(id)sender;
  658. - (void)showQEMUDoc:(id)sender;
  659. - (void)showQEMUTec:(id)sender;
  660. @end
  661. @implementation QemuCocoaAppController
  662. - (id) init
  663. {
  664. COCOA_DEBUG("QemuCocoaAppController: init\n");
  665. self = [super init];
  666. if (self) {
  667. // create a view and add it to the window
  668. cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)];
  669. if(!cocoaView) {
  670. fprintf(stderr, "(cocoa) can't create a view\n");
  671. exit(1);
  672. }
  673. // create a window
  674. normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame]
  675. styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask
  676. backing:NSBackingStoreBuffered defer:NO];
  677. if(!normalWindow) {
  678. fprintf(stderr, "(cocoa) can't create window\n");
  679. exit(1);
  680. }
  681. [normalWindow setAcceptsMouseMovedEvents:YES];
  682. [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]];
  683. [normalWindow setContentView:cocoaView];
  684. [normalWindow useOptimizedDrawing:YES];
  685. [normalWindow makeKeyAndOrderFront:self];
  686. [normalWindow center];
  687. }
  688. return self;
  689. }
  690. - (void) dealloc
  691. {
  692. COCOA_DEBUG("QemuCocoaAppController: dealloc\n");
  693. if (cocoaView)
  694. [cocoaView release];
  695. [super dealloc];
  696. }
  697. - (void)applicationDidFinishLaunching: (NSNotification *) note
  698. {
  699. COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
  700. // Display an open dialog box if no argument were passed or
  701. // if qemu was launched from the finder ( the Finder passes "-psn" )
  702. if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) {
  703. NSOpenPanel *op = [[NSOpenPanel alloc] init];
  704. [op setPrompt:@"Boot image"];
  705. [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
  706. [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil]
  707. modalForWindow:normalWindow modalDelegate:self
  708. didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
  709. } else {
  710. // or Launch Qemu, with the global args
  711. [self startEmulationWithArgc:gArgc argv:(char **)gArgv];
  712. }
  713. }
  714. - (void)applicationWillTerminate:(NSNotification *)aNotification
  715. {
  716. COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n");
  717. qemu_system_shutdown_request();
  718. exit(0);
  719. }
  720. - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
  721. {
  722. return YES;
  723. }
  724. - (void)startEmulationWithArgc:(int)argc argv:(char**)argv
  725. {
  726. COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
  727. int status;
  728. status = qemu_main(argc, argv, *_NSGetEnviron());
  729. exit(status);
  730. }
  731. - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
  732. {
  733. COCOA_DEBUG("QemuCocoaAppController: openPanelDidEnd\n");
  734. if(returnCode == NSCancelButton) {
  735. exit(0);
  736. } else if(returnCode == NSOKButton) {
  737. const char *bin = "qemu";
  738. char *img = (char*)[ [ sheet filename ] cStringUsingEncoding:NSASCIIStringEncoding];
  739. char **argv = (char**)malloc( sizeof(char*)*3 );
  740. [sheet close];
  741. asprintf(&argv[0], "%s", bin);
  742. asprintf(&argv[1], "-hda");
  743. asprintf(&argv[2], "%s", img);
  744. printf("Using argc %d argv %s -hda %s\n", 3, bin, img);
  745. [self startEmulationWithArgc:3 argv:(char**)argv];
  746. }
  747. }
  748. - (void)toggleFullScreen:(id)sender
  749. {
  750. COCOA_DEBUG("QemuCocoaAppController: toggleFullScreen\n");
  751. [cocoaView toggleFullScreen:sender];
  752. }
  753. - (void)showQEMUDoc:(id)sender
  754. {
  755. COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n");
  756. [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-doc.html",
  757. [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
  758. }
  759. - (void)showQEMUTec:(id)sender
  760. {
  761. COCOA_DEBUG("QemuCocoaAppController: showQEMUTec\n");
  762. [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-tech.html",
  763. [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
  764. }
  765. @end
  766. // Dock Connection
  767. typedef struct CPSProcessSerNum
  768. {
  769. UInt32 lo;
  770. UInt32 hi;
  771. } CPSProcessSerNum;
  772. OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
  773. OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
  774. OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
  775. int main (int argc, const char * argv[]) {
  776. gArgc = argc;
  777. gArgv = (char **)argv;
  778. CPSProcessSerNum PSN;
  779. int i;
  780. /* In case we don't need to display a window, let's not do that */
  781. for (i = 1; i < argc; i++) {
  782. const char *opt = argv[i];
  783. if (opt[0] == '-') {
  784. /* Treat --foo the same as -foo. */
  785. if (opt[1] == '-') {
  786. opt++;
  787. }
  788. if (!strcmp(opt, "-h") || !strcmp(opt, "-help") ||
  789. !strcmp(opt, "-vnc") ||
  790. !strcmp(opt, "-nographic") ||
  791. !strcmp(opt, "-version") ||
  792. !strcmp(opt, "-curses")) {
  793. return qemu_main(gArgc, gArgv, *_NSGetEnviron());
  794. }
  795. }
  796. }
  797. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  798. [NSApplication sharedApplication];
  799. if (!CPSGetCurrentProcess(&PSN))
  800. if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
  801. if (!CPSSetFrontProcess(&PSN))
  802. [NSApplication sharedApplication];
  803. // Add menus
  804. NSMenu *menu;
  805. NSMenuItem *menuItem;
  806. [NSApp setMainMenu:[[NSMenu alloc] init]];
  807. // Application menu
  808. menu = [[NSMenu alloc] initWithTitle:@""];
  809. [menu addItemWithTitle:@"About QEMU" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; // About QEMU
  810. [menu addItem:[NSMenuItem separatorItem]]; //Separator
  811. [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU
  812. menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others
  813. [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
  814. [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All
  815. [menu addItem:[NSMenuItem separatorItem]]; //Separator
  816. [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) keyEquivalent:@"q"];
  817. menuItem = [[NSMenuItem alloc] initWithTitle:@"Apple" action:nil keyEquivalent:@""];
  818. [menuItem setSubmenu:menu];
  819. [[NSApp mainMenu] addItem:menuItem];
  820. [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; // Workaround (this method is private since 10.4+)
  821. // View menu
  822. menu = [[NSMenu alloc] initWithTitle:@"View"];
  823. [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen
  824. menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease];
  825. [menuItem setSubmenu:menu];
  826. [[NSApp mainMenu] addItem:menuItem];
  827. // Window menu
  828. menu = [[NSMenu alloc] initWithTitle:@"Window"];
  829. [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"] autorelease]]; // Miniaturize
  830. menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease];
  831. [menuItem setSubmenu:menu];
  832. [[NSApp mainMenu] addItem:menuItem];
  833. [NSApp setWindowsMenu:menu];
  834. // Help menu
  835. menu = [[NSMenu alloc] initWithTitle:@"Help"];
  836. [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Documentation" action:@selector(showQEMUDoc:) keyEquivalent:@"?"] autorelease]]; // QEMU Help
  837. [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Technology" action:@selector(showQEMUTec:) keyEquivalent:@""] autorelease]]; // QEMU Help
  838. menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease];
  839. [menuItem setSubmenu:menu];
  840. [[NSApp mainMenu] addItem:menuItem];
  841. // Create an Application controller
  842. QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init];
  843. [NSApp setDelegate:appController];
  844. // Start the main event loop
  845. [NSApp run];
  846. [appController release];
  847. [pool release];
  848. return 0;
  849. }
  850. #pragma mark qemu
  851. static void cocoa_update(DisplayState *ds, int x, int y, int w, int h)
  852. {
  853. COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
  854. NSRect rect;
  855. if ([cocoaView cdx] == 1.0) {
  856. rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h);
  857. } else {
  858. rect = NSMakeRect(
  859. x * [cocoaView cdx],
  860. ([cocoaView gscreen].height - y - h) * [cocoaView cdy],
  861. w * [cocoaView cdx],
  862. h * [cocoaView cdy]);
  863. }
  864. [cocoaView setNeedsDisplayInRect:rect];
  865. }
  866. static void cocoa_resize(DisplayState *ds)
  867. {
  868. COCOA_DEBUG("qemu_cocoa: cocoa_resize\n");
  869. [cocoaView resizeContentToWidth:(int)(ds_get_width(ds)) height:(int)(ds_get_height(ds)) displayState:ds];
  870. }
  871. static void cocoa_refresh(DisplayState *ds)
  872. {
  873. COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
  874. if (kbd_mouse_is_absolute()) {
  875. if (![cocoaView isAbsoluteEnabled]) {
  876. if ([cocoaView isMouseGrabed]) {
  877. [cocoaView ungrabMouse];
  878. }
  879. }
  880. [cocoaView setAbsoluteEnabled:YES];
  881. }
  882. NSDate *distantPast;
  883. NSEvent *event;
  884. distantPast = [NSDate distantPast];
  885. do {
  886. event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
  887. inMode: NSDefaultRunLoopMode dequeue:YES];
  888. if (event != nil) {
  889. [cocoaView handleEvent:event];
  890. }
  891. } while(event != nil);
  892. vga_hw_update();
  893. }
  894. static void cocoa_cleanup(void)
  895. {
  896. COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n");
  897. g_free(dcl);
  898. }
  899. void cocoa_display_init(DisplayState *ds, int full_screen)
  900. {
  901. COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
  902. dcl = g_malloc0(sizeof(DisplayChangeListener));
  903. // register vga output callbacks
  904. dcl->dpy_update = cocoa_update;
  905. dcl->dpy_resize = cocoa_resize;
  906. dcl->dpy_refresh = cocoa_refresh;
  907. register_displaychangelistener(ds, dcl);
  908. // register cleanup function
  909. atexit(cocoa_cleanup);
  910. }