cocoa.m 40 KB

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