cocoa.m 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  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 isMouseDeassociated;
  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. /* The state surrounding mouse grabbing is potentially confusing.
  252. * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated
  253. * pointing device an absolute-position one?"], but is only updated on
  254. * next refresh.
  255. * isMouseGrabbed tracks whether GUI events are directed to the guest;
  256. * it controls whether special keys like Cmd get sent to the guest,
  257. * and whether we capture the mouse when in non-absolute mode.
  258. * isMouseDeassociated tracks whether we've told MacOSX to disassociate
  259. * the mouse and mouse cursor position by calling
  260. * CGAssociateMouseAndMouseCursorPosition(FALSE)
  261. * (which basically happens if we grab in non-absolute mode).
  262. */
  263. - (BOOL) isMouseGrabbed;
  264. - (BOOL) isAbsoluteEnabled;
  265. - (BOOL) isMouseDeassociated;
  266. - (float) cdx;
  267. - (float) cdy;
  268. - (QEMUScreen) gscreen;
  269. @end
  270. QemuCocoaView *cocoaView;
  271. @implementation QemuCocoaView
  272. - (id)initWithFrame:(NSRect)frameRect
  273. {
  274. COCOA_DEBUG("QemuCocoaView: initWithFrame\n");
  275. self = [super initWithFrame:frameRect];
  276. if (self) {
  277. screen.bitsPerComponent = 8;
  278. screen.bitsPerPixel = 32;
  279. screen.width = frameRect.size.width;
  280. screen.height = frameRect.size.height;
  281. }
  282. return self;
  283. }
  284. - (void) dealloc
  285. {
  286. COCOA_DEBUG("QemuCocoaView: dealloc\n");
  287. if (dataProviderRef)
  288. CGDataProviderRelease(dataProviderRef);
  289. [super dealloc];
  290. }
  291. - (BOOL) isOpaque
  292. {
  293. return YES;
  294. }
  295. - (BOOL) screenContainsPoint:(NSPoint) p
  296. {
  297. return (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height);
  298. }
  299. - (void) hideCursor
  300. {
  301. if (!cursor_hide) {
  302. return;
  303. }
  304. [NSCursor hide];
  305. }
  306. - (void) unhideCursor
  307. {
  308. if (!cursor_hide) {
  309. return;
  310. }
  311. [NSCursor unhide];
  312. }
  313. - (void) drawRect:(NSRect) rect
  314. {
  315. COCOA_DEBUG("QemuCocoaView: drawRect\n");
  316. // get CoreGraphic context
  317. CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort];
  318. CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
  319. CGContextSetShouldAntialias (viewContextRef, NO);
  320. // draw screen bitmap directly to Core Graphics context
  321. if (!dataProviderRef) {
  322. // Draw request before any guest device has set up a framebuffer:
  323. // just draw an opaque black rectangle
  324. CGContextSetRGBFillColor(viewContextRef, 0, 0, 0, 1.0);
  325. CGContextFillRect(viewContextRef, NSRectToCGRect(rect));
  326. } else {
  327. CGImageRef imageRef = CGImageCreate(
  328. screen.width, //width
  329. screen.height, //height
  330. screen.bitsPerComponent, //bitsPerComponent
  331. screen.bitsPerPixel, //bitsPerPixel
  332. (screen.width * (screen.bitsPerComponent/2)), //bytesPerRow
  333. #ifdef __LITTLE_ENDIAN__
  334. CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace for OS X >= 10.4
  335. kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
  336. #else
  337. CGColorSpaceCreateDeviceRGB(), //colorspace for OS X < 10.4 (actually ppc)
  338. kCGImageAlphaNoneSkipFirst, //bitmapInfo
  339. #endif
  340. dataProviderRef, //provider
  341. NULL, //decode
  342. 0, //interpolate
  343. kCGRenderingIntentDefault //intent
  344. );
  345. // test if host supports "CGImageCreateWithImageInRect" at compile time
  346. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
  347. if (CGImageCreateWithImageInRect == NULL) { // test if "CGImageCreateWithImageInRect" is supported on host at runtime
  348. #endif
  349. // compatibility drawing code (draws everything) (OS X < 10.4)
  350. CGContextDrawImage (viewContextRef, CGRectMake(0, 0, [self bounds].size.width, [self bounds].size.height), imageRef);
  351. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
  352. } else {
  353. // selective drawing code (draws only dirty rectangles) (OS X >= 10.4)
  354. const NSRect *rectList;
  355. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
  356. NSInteger rectCount;
  357. #else
  358. int rectCount;
  359. #endif
  360. int i;
  361. CGImageRef clipImageRef;
  362. CGRect clipRect;
  363. [self getRectsBeingDrawn:&rectList count:&rectCount];
  364. for (i = 0; i < rectCount; i++) {
  365. clipRect.origin.x = rectList[i].origin.x / cdx;
  366. clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy;
  367. clipRect.size.width = rectList[i].size.width / cdx;
  368. clipRect.size.height = rectList[i].size.height / cdy;
  369. clipImageRef = CGImageCreateWithImageInRect(
  370. imageRef,
  371. clipRect
  372. );
  373. CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
  374. CGImageRelease (clipImageRef);
  375. }
  376. }
  377. #endif
  378. CGImageRelease (imageRef);
  379. }
  380. }
  381. - (void) setContentDimensions
  382. {
  383. COCOA_DEBUG("QemuCocoaView: setContentDimensions\n");
  384. if (isFullscreen) {
  385. cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width;
  386. cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height;
  387. cw = screen.width * cdx;
  388. ch = screen.height * cdy;
  389. cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0;
  390. cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0;
  391. } else {
  392. cx = 0;
  393. cy = 0;
  394. cw = screen.width;
  395. ch = screen.height;
  396. cdx = 1.0;
  397. cdy = 1.0;
  398. }
  399. }
  400. - (void) switchSurface:(DisplaySurface *)surface
  401. {
  402. COCOA_DEBUG("QemuCocoaView: switchSurface\n");
  403. int w = surface_width(surface);
  404. int h = surface_height(surface);
  405. /* cdx == 0 means this is our very first surface, in which case we need
  406. * to recalculate the content dimensions even if it happens to be the size
  407. * of the initial empty window.
  408. */
  409. bool isResize = (w != screen.width || h != screen.height || cdx == 0.0);
  410. int oldh = screen.height;
  411. if (isResize) {
  412. // Resize before we trigger the redraw, or we'll redraw at the wrong size
  413. COCOA_DEBUG("switchSurface: new size %d x %d\n", w, h);
  414. screen.width = w;
  415. screen.height = h;
  416. [self setContentDimensions];
  417. [self setFrame:NSMakeRect(cx, cy, cw, ch)];
  418. }
  419. // update screenBuffer
  420. if (dataProviderRef)
  421. CGDataProviderRelease(dataProviderRef);
  422. //sync host window color space with guests
  423. screen.bitsPerPixel = surface_bits_per_pixel(surface);
  424. screen.bitsPerComponent = surface_bytes_per_pixel(surface) * 2;
  425. dataProviderRef = CGDataProviderCreateWithData(NULL, surface_data(surface), w * 4 * h, NULL);
  426. // update windows
  427. if (isFullscreen) {
  428. [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]];
  429. [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:NO animate:NO];
  430. } else {
  431. if (qemu_name)
  432. [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
  433. [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:YES animate:NO];
  434. }
  435. if (isResize) {
  436. [normalWindow center];
  437. }
  438. }
  439. - (void) toggleFullScreen:(id)sender
  440. {
  441. COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n");
  442. if (isFullscreen) { // switch from fullscreen to desktop
  443. isFullscreen = FALSE;
  444. [self ungrabMouse];
  445. [self setContentDimensions];
  446. // test if host supports "exitFullScreenModeWithOptions" at compile time
  447. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
  448. if ([NSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)]) { // test if "exitFullScreenModeWithOptions" is supported on host at runtime
  449. [self exitFullScreenModeWithOptions:nil];
  450. } else {
  451. #endif
  452. [fullScreenWindow close];
  453. [normalWindow setContentView: self];
  454. [normalWindow makeKeyAndOrderFront: self];
  455. [NSMenu setMenuBarVisible:YES];
  456. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
  457. }
  458. #endif
  459. } else { // switch from desktop to fullscreen
  460. isFullscreen = TRUE;
  461. [self grabMouse];
  462. [self setContentDimensions];
  463. // test if host supports "enterFullScreenMode:withOptions" at compile time
  464. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
  465. if ([NSView respondsToSelector:@selector(enterFullScreenMode:withOptions:)]) { // test if "enterFullScreenMode:withOptions" is supported on host at runtime
  466. [self enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys:
  467. [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens,
  468. [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kCGDisplayModeIsStretched, nil], NSFullScreenModeSetting,
  469. nil]];
  470. } else {
  471. #endif
  472. [NSMenu setMenuBarVisible:NO];
  473. fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame]
  474. styleMask:NSBorderlessWindowMask
  475. backing:NSBackingStoreBuffered
  476. defer:NO];
  477. [fullScreenWindow setHasShadow:NO];
  478. [fullScreenWindow setContentView:self];
  479. [fullScreenWindow makeKeyAndOrderFront:self];
  480. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
  481. }
  482. #endif
  483. }
  484. }
  485. - (void) handleEvent:(NSEvent *)event
  486. {
  487. COCOA_DEBUG("QemuCocoaView: handleEvent\n");
  488. int buttons = 0;
  489. int keycode;
  490. bool mouse_event = false;
  491. NSPoint p = [event locationInWindow];
  492. switch ([event type]) {
  493. case NSFlagsChanged:
  494. keycode = cocoa_keycode_to_qemu([event keyCode]);
  495. if ((keycode == 219 || keycode == 220) && !isMouseGrabbed) {
  496. /* Don't pass command key changes to guest unless mouse is grabbed */
  497. keycode = 0;
  498. }
  499. if (keycode) {
  500. if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup
  501. qemu_input_event_send_key_number(dcl->con, keycode, true);
  502. qemu_input_event_send_key_number(dcl->con, keycode, false);
  503. } else if (qemu_console_is_graphic(NULL)) {
  504. if (modifiers_state[keycode] == 0) { // keydown
  505. qemu_input_event_send_key_number(dcl->con, keycode, true);
  506. modifiers_state[keycode] = 1;
  507. } else { // keyup
  508. qemu_input_event_send_key_number(dcl->con, keycode, false);
  509. modifiers_state[keycode] = 0;
  510. }
  511. }
  512. }
  513. // release Mouse grab when pressing ctrl+alt
  514. if (!isFullscreen && ([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
  515. [self ungrabMouse];
  516. }
  517. break;
  518. case NSKeyDown:
  519. keycode = cocoa_keycode_to_qemu([event keyCode]);
  520. // forward command key combos to the host UI unless the mouse is grabbed
  521. if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) {
  522. [NSApp sendEvent:event];
  523. return;
  524. }
  525. // default
  526. // handle control + alt Key Combos (ctrl+alt is reserved for QEMU)
  527. if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
  528. switch (keycode) {
  529. // enable graphic console
  530. case 0x02 ... 0x0a: // '1' to '9' keys
  531. console_select(keycode - 0x02);
  532. break;
  533. }
  534. // handle keys for graphic console
  535. } else if (qemu_console_is_graphic(NULL)) {
  536. qemu_input_event_send_key_number(dcl->con, keycode, true);
  537. // handlekeys for Monitor
  538. } else {
  539. int keysym = 0;
  540. switch([event keyCode]) {
  541. case 115:
  542. keysym = QEMU_KEY_HOME;
  543. break;
  544. case 117:
  545. keysym = QEMU_KEY_DELETE;
  546. break;
  547. case 119:
  548. keysym = QEMU_KEY_END;
  549. break;
  550. case 123:
  551. keysym = QEMU_KEY_LEFT;
  552. break;
  553. case 124:
  554. keysym = QEMU_KEY_RIGHT;
  555. break;
  556. case 125:
  557. keysym = QEMU_KEY_DOWN;
  558. break;
  559. case 126:
  560. keysym = QEMU_KEY_UP;
  561. break;
  562. default:
  563. {
  564. NSString *ks = [event characters];
  565. if ([ks length] > 0)
  566. keysym = [ks characterAtIndex:0];
  567. }
  568. }
  569. if (keysym)
  570. kbd_put_keysym(keysym);
  571. }
  572. break;
  573. case NSKeyUp:
  574. keycode = cocoa_keycode_to_qemu([event keyCode]);
  575. // don't pass the guest a spurious key-up if we treated this
  576. // command-key combo as a host UI action
  577. if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) {
  578. return;
  579. }
  580. if (qemu_console_is_graphic(NULL)) {
  581. qemu_input_event_send_key_number(dcl->con, keycode, false);
  582. }
  583. break;
  584. case NSMouseMoved:
  585. if (isAbsoluteEnabled) {
  586. if (![self screenContainsPoint:p] || ![[self window] isKeyWindow]) {
  587. if (isMouseGrabbed) {
  588. [self ungrabMouse];
  589. }
  590. } else {
  591. if (!isMouseGrabbed) {
  592. [self grabMouse];
  593. }
  594. }
  595. }
  596. mouse_event = true;
  597. break;
  598. case NSLeftMouseDown:
  599. if ([event modifierFlags] & NSCommandKeyMask) {
  600. buttons |= MOUSE_EVENT_RBUTTON;
  601. } else {
  602. buttons |= MOUSE_EVENT_LBUTTON;
  603. }
  604. mouse_event = true;
  605. break;
  606. case NSRightMouseDown:
  607. buttons |= MOUSE_EVENT_RBUTTON;
  608. mouse_event = true;
  609. break;
  610. case NSOtherMouseDown:
  611. buttons |= MOUSE_EVENT_MBUTTON;
  612. mouse_event = true;
  613. break;
  614. case NSLeftMouseDragged:
  615. if ([event modifierFlags] & NSCommandKeyMask) {
  616. buttons |= MOUSE_EVENT_RBUTTON;
  617. } else {
  618. buttons |= MOUSE_EVENT_LBUTTON;
  619. }
  620. mouse_event = true;
  621. break;
  622. case NSRightMouseDragged:
  623. buttons |= MOUSE_EVENT_RBUTTON;
  624. mouse_event = true;
  625. break;
  626. case NSOtherMouseDragged:
  627. buttons |= MOUSE_EVENT_MBUTTON;
  628. mouse_event = true;
  629. break;
  630. case NSLeftMouseUp:
  631. mouse_event = true;
  632. if (!isMouseGrabbed && [self screenContainsPoint:p]) {
  633. [self grabMouse];
  634. }
  635. break;
  636. case NSRightMouseUp:
  637. mouse_event = true;
  638. break;
  639. case NSOtherMouseUp:
  640. mouse_event = true;
  641. break;
  642. case NSScrollWheel:
  643. if (isMouseGrabbed) {
  644. buttons |= ([event deltaY] < 0) ?
  645. MOUSE_EVENT_WHEELUP : MOUSE_EVENT_WHEELDN;
  646. }
  647. mouse_event = true;
  648. break;
  649. default:
  650. [NSApp sendEvent:event];
  651. }
  652. if (mouse_event) {
  653. if (last_buttons != buttons) {
  654. static uint32_t bmap[INPUT_BUTTON_MAX] = {
  655. [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON,
  656. [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
  657. [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
  658. [INPUT_BUTTON_WHEEL_UP] = MOUSE_EVENT_WHEELUP,
  659. [INPUT_BUTTON_WHEEL_DOWN] = MOUSE_EVENT_WHEELDN,
  660. };
  661. qemu_input_update_buttons(dcl->con, bmap, last_buttons, buttons);
  662. last_buttons = buttons;
  663. }
  664. if (isMouseGrabbed) {
  665. if (isAbsoluteEnabled) {
  666. /* Note that the origin for Cocoa mouse coords is bottom left, not top left.
  667. * The check on screenContainsPoint is to avoid sending out of range values for
  668. * clicks in the titlebar.
  669. */
  670. if ([self screenContainsPoint:p]) {
  671. qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, screen.width);
  672. qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, screen.height - p.y, screen.height);
  673. }
  674. } else {
  675. qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, (int)[event deltaX]);
  676. qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]);
  677. }
  678. } else {
  679. [NSApp sendEvent:event];
  680. }
  681. qemu_input_event_sync();
  682. }
  683. }
  684. - (void) grabMouse
  685. {
  686. COCOA_DEBUG("QemuCocoaView: grabMouse\n");
  687. if (!isFullscreen) {
  688. if (qemu_name)
  689. [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt to release Mouse)", qemu_name]];
  690. else
  691. [normalWindow setTitle:@"QEMU - (Press ctrl + alt to release Mouse)"];
  692. }
  693. [self hideCursor];
  694. if (!isAbsoluteEnabled) {
  695. isMouseDeassociated = TRUE;
  696. CGAssociateMouseAndMouseCursorPosition(FALSE);
  697. }
  698. isMouseGrabbed = TRUE; // while isMouseGrabbed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:]
  699. }
  700. - (void) ungrabMouse
  701. {
  702. COCOA_DEBUG("QemuCocoaView: ungrabMouse\n");
  703. if (!isFullscreen) {
  704. if (qemu_name)
  705. [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
  706. else
  707. [normalWindow setTitle:@"QEMU"];
  708. }
  709. [self unhideCursor];
  710. if (isMouseDeassociated) {
  711. CGAssociateMouseAndMouseCursorPosition(TRUE);
  712. isMouseDeassociated = FALSE;
  713. }
  714. isMouseGrabbed = FALSE;
  715. }
  716. - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;}
  717. - (BOOL) isMouseGrabbed {return isMouseGrabbed;}
  718. - (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;}
  719. - (BOOL) isMouseDeassociated {return isMouseDeassociated;}
  720. - (float) cdx {return cdx;}
  721. - (float) cdy {return cdy;}
  722. - (QEMUScreen) gscreen {return screen;}
  723. @end
  724. /*
  725. ------------------------------------------------------
  726. QemuCocoaAppController
  727. ------------------------------------------------------
  728. */
  729. @interface QemuCocoaAppController : NSObject
  730. {
  731. }
  732. - (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
  733. - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
  734. - (void)toggleFullScreen:(id)sender;
  735. - (void)showQEMUDoc:(id)sender;
  736. - (void)showQEMUTec:(id)sender;
  737. @end
  738. @implementation QemuCocoaAppController
  739. - (id) init
  740. {
  741. COCOA_DEBUG("QemuCocoaAppController: init\n");
  742. self = [super init];
  743. if (self) {
  744. // create a view and add it to the window
  745. cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)];
  746. if(!cocoaView) {
  747. fprintf(stderr, "(cocoa) can't create a view\n");
  748. exit(1);
  749. }
  750. // create a window
  751. normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame]
  752. styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask
  753. backing:NSBackingStoreBuffered defer:NO];
  754. if(!normalWindow) {
  755. fprintf(stderr, "(cocoa) can't create window\n");
  756. exit(1);
  757. }
  758. [normalWindow setAcceptsMouseMovedEvents:YES];
  759. [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]];
  760. [normalWindow setContentView:cocoaView];
  761. [normalWindow useOptimizedDrawing:YES];
  762. [normalWindow makeKeyAndOrderFront:self];
  763. [normalWindow center];
  764. }
  765. return self;
  766. }
  767. - (void) dealloc
  768. {
  769. COCOA_DEBUG("QemuCocoaAppController: dealloc\n");
  770. if (cocoaView)
  771. [cocoaView release];
  772. [super dealloc];
  773. }
  774. - (void)applicationDidFinishLaunching: (NSNotification *) note
  775. {
  776. COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
  777. // Display an open dialog box if no arguments were passed or
  778. // if qemu was launched from the finder ( the Finder passes "-psn" )
  779. if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) {
  780. NSOpenPanel *op = [[NSOpenPanel alloc] init];
  781. [op setPrompt:@"Boot image"];
  782. [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
  783. NSArray *filetypes = [NSArray arrayWithObjects:@"img", @"iso", @"dmg",
  784. @"qcow", @"qcow2", @"cloop", @"vmdk", nil];
  785. #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
  786. [op setAllowedFileTypes:filetypes];
  787. [op beginSheetModalForWindow:normalWindow
  788. completionHandler:^(NSInteger returnCode)
  789. { [self openPanelDidEnd:op
  790. returnCode:returnCode contextInfo:NULL ]; } ];
  791. #else
  792. // Compatibility code for pre-10.6, using deprecated method
  793. [op beginSheetForDirectory:nil file:nil types:filetypes
  794. modalForWindow:normalWindow modalDelegate:self
  795. didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
  796. #endif
  797. } else {
  798. // or launch QEMU, with the global args
  799. [self startEmulationWithArgc:gArgc argv:(char **)gArgv];
  800. }
  801. }
  802. - (void)applicationWillTerminate:(NSNotification *)aNotification
  803. {
  804. COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n");
  805. qemu_system_shutdown_request();
  806. exit(0);
  807. }
  808. - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
  809. {
  810. return YES;
  811. }
  812. - (void)startEmulationWithArgc:(int)argc argv:(char**)argv
  813. {
  814. COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
  815. int status;
  816. status = qemu_main(argc, argv, *_NSGetEnviron());
  817. exit(status);
  818. }
  819. - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
  820. {
  821. COCOA_DEBUG("QemuCocoaAppController: openPanelDidEnd\n");
  822. if(returnCode == NSCancelButton) {
  823. exit(0);
  824. } else if(returnCode == NSOKButton) {
  825. char *img = (char*)[ [ [ sheet URL ] path ] cStringUsingEncoding:NSASCIIStringEncoding];
  826. char **argv = g_new(char *, 4);
  827. [sheet close];
  828. argv[0] = g_strdup(gArgv[0]);
  829. argv[1] = g_strdup("-hda");
  830. argv[2] = g_strdup(img);
  831. argv[3] = NULL;
  832. // printf("Using argc %d argv %s -hda %s\n", 3, gArgv[0], img);
  833. [self startEmulationWithArgc:3 argv:(char**)argv];
  834. }
  835. }
  836. - (void)toggleFullScreen:(id)sender
  837. {
  838. COCOA_DEBUG("QemuCocoaAppController: toggleFullScreen\n");
  839. [cocoaView toggleFullScreen:sender];
  840. }
  841. - (void)showQEMUDoc:(id)sender
  842. {
  843. COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n");
  844. [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-doc.html",
  845. [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
  846. }
  847. - (void)showQEMUTec:(id)sender
  848. {
  849. COCOA_DEBUG("QemuCocoaAppController: showQEMUTec\n");
  850. [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-tech.html",
  851. [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
  852. }
  853. @end
  854. int main (int argc, const char * argv[]) {
  855. gArgc = argc;
  856. gArgv = (char **)argv;
  857. int i;
  858. /* In case we don't need to display a window, let's not do that */
  859. for (i = 1; i < argc; i++) {
  860. const char *opt = argv[i];
  861. if (opt[0] == '-') {
  862. /* Treat --foo the same as -foo. */
  863. if (opt[1] == '-') {
  864. opt++;
  865. }
  866. if (!strcmp(opt, "-h") || !strcmp(opt, "-help") ||
  867. !strcmp(opt, "-vnc") ||
  868. !strcmp(opt, "-nographic") ||
  869. !strcmp(opt, "-version") ||
  870. !strcmp(opt, "-curses") ||
  871. !strcmp(opt, "-qtest")) {
  872. return qemu_main(gArgc, gArgv, *_NSGetEnviron());
  873. }
  874. }
  875. }
  876. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  877. // Pull this console process up to being a fully-fledged graphical
  878. // app with a menubar and Dock icon
  879. ProcessSerialNumber psn = { 0, kCurrentProcess };
  880. TransformProcessType(&psn, kProcessTransformToForegroundApplication);
  881. [NSApplication sharedApplication];
  882. // Add menus
  883. NSMenu *menu;
  884. NSMenuItem *menuItem;
  885. [NSApp setMainMenu:[[NSMenu alloc] init]];
  886. // Application menu
  887. menu = [[NSMenu alloc] initWithTitle:@""];
  888. [menu addItemWithTitle:@"About QEMU" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; // About QEMU
  889. [menu addItem:[NSMenuItem separatorItem]]; //Separator
  890. [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU
  891. menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others
  892. [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
  893. [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All
  894. [menu addItem:[NSMenuItem separatorItem]]; //Separator
  895. [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) keyEquivalent:@"q"];
  896. menuItem = [[NSMenuItem alloc] initWithTitle:@"Apple" action:nil keyEquivalent:@""];
  897. [menuItem setSubmenu:menu];
  898. [[NSApp mainMenu] addItem:menuItem];
  899. [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; // Workaround (this method is private since 10.4+)
  900. // View menu
  901. menu = [[NSMenu alloc] initWithTitle:@"View"];
  902. [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen
  903. menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease];
  904. [menuItem setSubmenu:menu];
  905. [[NSApp mainMenu] addItem:menuItem];
  906. // Window menu
  907. menu = [[NSMenu alloc] initWithTitle:@"Window"];
  908. [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"] autorelease]]; // Miniaturize
  909. menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease];
  910. [menuItem setSubmenu:menu];
  911. [[NSApp mainMenu] addItem:menuItem];
  912. [NSApp setWindowsMenu:menu];
  913. // Help menu
  914. menu = [[NSMenu alloc] initWithTitle:@"Help"];
  915. [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Documentation" action:@selector(showQEMUDoc:) keyEquivalent:@"?"] autorelease]]; // QEMU Help
  916. [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Technology" action:@selector(showQEMUTec:) keyEquivalent:@""] autorelease]]; // QEMU Help
  917. menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease];
  918. [menuItem setSubmenu:menu];
  919. [[NSApp mainMenu] addItem:menuItem];
  920. // Create an Application controller
  921. QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init];
  922. [NSApp setDelegate:appController];
  923. // Start the main event loop
  924. [NSApp run];
  925. [appController release];
  926. [pool release];
  927. return 0;
  928. }
  929. #pragma mark qemu
  930. static void cocoa_update(DisplayChangeListener *dcl,
  931. int x, int y, int w, int h)
  932. {
  933. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  934. COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
  935. NSRect rect;
  936. if ([cocoaView cdx] == 1.0) {
  937. rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h);
  938. } else {
  939. rect = NSMakeRect(
  940. x * [cocoaView cdx],
  941. ([cocoaView gscreen].height - y - h) * [cocoaView cdy],
  942. w * [cocoaView cdx],
  943. h * [cocoaView cdy]);
  944. }
  945. [cocoaView setNeedsDisplayInRect:rect];
  946. [pool release];
  947. }
  948. static void cocoa_switch(DisplayChangeListener *dcl,
  949. DisplaySurface *surface)
  950. {
  951. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  952. COCOA_DEBUG("qemu_cocoa: cocoa_switch\n");
  953. [cocoaView switchSurface:surface];
  954. [pool release];
  955. }
  956. static void cocoa_refresh(DisplayChangeListener *dcl)
  957. {
  958. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  959. COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
  960. if (qemu_input_is_absolute()) {
  961. if (![cocoaView isAbsoluteEnabled]) {
  962. if ([cocoaView isMouseGrabbed]) {
  963. [cocoaView ungrabMouse];
  964. }
  965. }
  966. [cocoaView setAbsoluteEnabled:YES];
  967. }
  968. NSDate *distantPast;
  969. NSEvent *event;
  970. distantPast = [NSDate distantPast];
  971. do {
  972. event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
  973. inMode: NSDefaultRunLoopMode dequeue:YES];
  974. if (event != nil) {
  975. [cocoaView handleEvent:event];
  976. }
  977. } while(event != nil);
  978. graphic_hw_update(NULL);
  979. [pool release];
  980. }
  981. static void cocoa_cleanup(void)
  982. {
  983. COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n");
  984. g_free(dcl);
  985. }
  986. static const DisplayChangeListenerOps dcl_ops = {
  987. .dpy_name = "cocoa",
  988. .dpy_gfx_update = cocoa_update,
  989. .dpy_gfx_switch = cocoa_switch,
  990. .dpy_refresh = cocoa_refresh,
  991. };
  992. void cocoa_display_init(DisplayState *ds, int full_screen)
  993. {
  994. COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
  995. dcl = g_malloc0(sizeof(DisplayChangeListener));
  996. // register vga output callbacks
  997. dcl->ops = &dcl_ops;
  998. register_displaychangelistener(dcl);
  999. // register cleanup function
  1000. atexit(cocoa_cleanup);
  1001. }