]>
Commit | Line | Data |
---|---|---|
5b0753e0 FB |
1 | /* |
2 | * QEMU Cocoa display driver | |
3 | * | |
4 | * Copyright (c) 2005 Pierre d'Herbemont | |
5 | * many code/inspiration from SDL 1.2 code (LGPL) | |
6 | * | |
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | * of this software and associated documentation files (the "Software"), to deal | |
9 | * in the Software without restriction, including without limitation the rights | |
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | * copies of the Software, and to permit persons to whom the Software is | |
12 | * furnished to do so, subject to the following conditions: | |
13 | * | |
14 | * The above copyright notice and this permission notice shall be included in | |
15 | * all copies or substantial portions of the Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | * THE SOFTWARE. | |
24 | */ | |
25 | /* | |
26 | Todo : x miniaturize window | |
27 | x center the window | |
28 | - save window position | |
29 | - handle keyboard event | |
30 | - handle mouse event | |
31 | - non 32 bpp support | |
32 | - full screen | |
33 | - mouse focus | |
34 | x simple graphical prompt to demo | |
35 | - better graphical prompt | |
36 | */ | |
da4dbf74 | 37 | |
5b0753e0 FB |
38 | #import <Cocoa/Cocoa.h> |
39 | ||
da4dbf74 FB |
40 | #include "vl.h" |
41 | ||
5b0753e0 FB |
42 | NSWindow *window = NULL; |
43 | NSQuickDrawView *qd_view = NULL; | |
44 | ||
45 | ||
46 | int gArgc; | |
47 | char **gArgv; | |
48 | DisplayState current_ds; | |
49 | ||
87f48e6a FB |
50 | int grab = 0; |
51 | int modifiers_state[256]; | |
52 | ||
5b0753e0 FB |
53 | /* main defined in qemu/vl.c */ |
54 | int qemu_main(int argc, char **argv); | |
55 | ||
56 | /* To deal with miniaturization */ | |
57 | @interface QemuWindow : NSWindow | |
58 | { } | |
59 | @end | |
60 | ||
61 | ||
62 | /* | |
63 | ------------------------------------------------------ | |
64 | Qemu Video Driver | |
65 | ------------------------------------------------------ | |
66 | */ | |
67 | ||
68 | /* | |
69 | ------------------------------------------------------ | |
70 | cocoa_update | |
71 | ------------------------------------------------------ | |
72 | */ | |
73 | static void cocoa_update(DisplayState *ds, int x, int y, int w, int h) | |
74 | { | |
75 | //printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); | |
76 | ||
77 | /* Use QDFlushPortBuffer() to flush content to display */ | |
78 | RgnHandle dirty = NewRgn (); | |
79 | RgnHandle temp = NewRgn (); | |
80 | ||
81 | SetEmptyRgn (dirty); | |
82 | ||
83 | /* Build the region of dirty rectangles */ | |
84 | MacSetRectRgn (temp, x, y, | |
85 | x + w, y + h); | |
86 | MacUnionRgn (dirty, temp, dirty); | |
87 | ||
88 | /* Flush the dirty region */ | |
89 | QDFlushPortBuffer ( [ qd_view qdPort ], dirty ); | |
90 | DisposeRgn (dirty); | |
91 | DisposeRgn (temp); | |
92 | } | |
93 | ||
94 | /* | |
95 | ------------------------------------------------------ | |
96 | cocoa_resize | |
97 | ------------------------------------------------------ | |
98 | */ | |
99 | static void cocoa_resize(DisplayState *ds, int w, int h) | |
100 | { | |
101 | const int device_bpp = 32; | |
102 | static void *screen_pixels; | |
103 | static int screen_pitch; | |
104 | NSRect contentRect; | |
105 | ||
106 | //printf("resizing to %d %d\n", w, h); | |
107 | ||
108 | contentRect = NSMakeRect (0, 0, w, h); | |
109 | if(window) | |
110 | { | |
111 | [window close]; | |
112 | [window release]; | |
113 | } | |
114 | window = [ [ QemuWindow alloc ] initWithContentRect:contentRect | |
115 | styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask | |
116 | backing:NSBackingStoreBuffered defer:NO]; | |
117 | if(!window) | |
118 | { | |
119 | fprintf(stderr, "(cocoa) can't create window\n"); | |
120 | exit(1); | |
121 | } | |
122 | ||
123 | if(qd_view) | |
124 | [qd_view release]; | |
125 | ||
126 | qd_view = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ]; | |
127 | ||
128 | if(!qd_view) | |
129 | { | |
130 | fprintf(stderr, "(cocoa) can't create qd_view\n"); | |
131 | exit(1); | |
132 | } | |
133 | ||
134 | [ window setAcceptsMouseMovedEvents:YES ]; | |
135 | [ window setTitle:@"Qemu" ]; | |
136 | [ window setReleasedWhenClosed:NO ]; | |
137 | ||
138 | /* Set screen to black */ | |
139 | [ window setBackgroundColor: [NSColor blackColor] ]; | |
140 | ||
141 | /* set window position */ | |
142 | [ window center ]; | |
143 | ||
144 | [ qd_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; | |
145 | [ [ window contentView ] addSubview:qd_view ]; | |
146 | [ qd_view release ]; | |
147 | [ window makeKeyAndOrderFront:nil ]; | |
148 | ||
149 | /* Careful here, the window seems to have to be onscreen to do that */ | |
150 | LockPortBits ( [ qd_view qdPort ] ); | |
151 | screen_pixels = GetPixBaseAddr ( GetPortPixMap ( [ qd_view qdPort ] ) ); | |
152 | screen_pitch = GetPixRowBytes ( GetPortPixMap ( [ qd_view qdPort ] ) ); | |
153 | UnlockPortBits ( [ qd_view qdPort ] ); | |
154 | { | |
155 | int vOffset = [ window frame ].size.height - | |
156 | [ qd_view frame ].size.height - [ qd_view frame ].origin.y; | |
157 | ||
158 | int hOffset = [ qd_view frame ].origin.x; | |
159 | ||
160 | screen_pixels += (vOffset * screen_pitch) + hOffset * (device_bpp/8); | |
161 | } | |
162 | ds->data = screen_pixels; | |
163 | ds->linesize = screen_pitch; | |
164 | ds->depth = device_bpp; | |
165 | ds->width = w; | |
166 | ds->height = h; | |
167 | ||
168 | current_ds = *ds; | |
169 | } | |
170 | ||
171 | /* | |
172 | ------------------------------------------------------ | |
173 | keymap conversion | |
174 | ------------------------------------------------------ | |
175 | */ | |
176 | ||
87f48e6a | 177 | int keymap[] = |
5b0753e0 | 178 | { |
87f48e6a FB |
179 | // SdlI macI macH SdlH 104xtH 104xtC sdl |
180 | 30, // 0 0x00 0x1e A QZ_a | |
181 | 31, // 1 0x01 0x1f S QZ_s | |
182 | 32, // 2 0x02 0x20 D QZ_d | |
183 | 33, // 3 0x03 0x21 F QZ_f | |
184 | 35, // 4 0x04 0x23 H QZ_h | |
185 | 34, // 5 0x05 0x22 G QZ_g | |
186 | 44, // 6 0x06 0x2c Z QZ_z | |
187 | 45, // 7 0x07 0x2d X QZ_x | |
188 | 46, // 8 0x08 0x2e C QZ_c | |
189 | 47, // 9 0x09 0x2f V QZ_v | |
190 | 0, // 10 0x0A Undefined | |
191 | 48, // 11 0x0B 0x30 B QZ_b | |
192 | 16, // 12 0x0C 0x10 Q QZ_q | |
193 | 17, // 13 0x0D 0x11 W QZ_w | |
194 | 18, // 14 0x0E 0x12 E QZ_e | |
195 | 19, // 15 0x0F 0x13 R QZ_r | |
196 | 21, // 16 0x10 0x15 Y QZ_y | |
197 | 20, // 17 0x11 0x14 T QZ_t | |
198 | 2, // 18 0x12 0x02 1 QZ_1 | |
199 | 3, // 19 0x13 0x03 2 QZ_2 | |
200 | 4, // 20 0x14 0x04 3 QZ_3 | |
201 | 5, // 21 0x15 0x05 4 QZ_4 | |
202 | 7, // 22 0x16 0x07 6 QZ_6 | |
203 | 6, // 23 0x17 0x06 5 QZ_5 | |
204 | 13, // 24 0x18 0x0d = QZ_EQUALS | |
205 | 10, // 25 0x19 0x0a 9 QZ_9 | |
206 | 8, // 26 0x1A 0x08 7 QZ_7 | |
207 | 12, // 27 0x1B 0x0c - QZ_MINUS | |
208 | 9, // 28 0x1C 0x09 8 QZ_8 | |
209 | 11, // 29 0x1D 0x0b 0 QZ_0 | |
210 | 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET | |
211 | 24, // 31 0x1F 0x18 O QZ_o | |
212 | 22, // 32 0x20 0x16 U QZ_u | |
213 | 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET | |
214 | 23, // 34 0x22 0x17 I QZ_i | |
215 | 25, // 35 0x23 0x19 P QZ_p | |
216 | 28, // 36 0x24 0x1c ENTER QZ_RETURN | |
217 | 38, // 37 0x25 0x26 L QZ_l | |
218 | 36, // 38 0x26 0x24 J QZ_j | |
219 | 40, // 39 0x27 0x28 ' QZ_QUOTE | |
220 | 37, // 40 0x28 0x25 K QZ_k | |
221 | 39, // 41 0x29 0x27 ; QZ_SEMICOLON | |
222 | 43, // 42 0x2A 0x2b \ QZ_BACKSLASH | |
223 | 51, // 43 0x2B 0x33 , QZ_COMMA | |
224 | 53, // 44 0x2C 0x35 / QZ_SLASH | |
225 | 49, // 45 0x2D 0x31 N QZ_n | |
226 | 50, // 46 0x2E 0x32 M QZ_m | |
227 | 52, // 47 0x2F 0x34 . QZ_PERIOD | |
228 | 15, // 48 0x30 0x0f TAB QZ_TAB | |
229 | 57, // 49 0x31 0x39 SPACE QZ_SPACE | |
230 | 41, // 50 0x32 0x29 ` QZ_BACKQUOTE | |
231 | 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE | |
232 | 0, // 52 0x34 Undefined | |
233 | 1, // 53 0x35 0x01 ESC QZ_ESCAPE | |
234 | 0, // 54 0x36 QZ_RMETA | |
235 | 0, // 55 0x37 QZ_LMETA | |
236 | 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT | |
237 | 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK | |
238 | 56, // 58 0x3A 0x38 L ALT QZ_LALT | |
239 | 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL | |
240 | 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT | |
241 | 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT | |
242 | 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL | |
243 | 0, // 63 0x3F Undefined | |
244 | 0, // 64 0x40 Undefined | |
245 | 0, // 65 0x41 Undefined | |
246 | 0, // 66 0x42 Undefined | |
247 | 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY | |
248 | 0, // 68 0x44 Undefined | |
249 | 78, // 69 0x45 0x4e KP + QZ_KP_PLUS | |
250 | 0, // 70 0x46 Undefined | |
251 | 69, // 71 0x47 0x45 NUM QZ_NUMLOCK | |
252 | 0, // 72 0x48 Undefined | |
253 | 0, // 73 0x49 Undefined | |
254 | 0, // 74 0x4A Undefined | |
255 | 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE | |
256 | 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER | |
257 | 0, // 77 0x4D undefined | |
258 | 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS | |
259 | 0, // 79 0x4F Undefined | |
260 | 0, // 80 0x50 Undefined | |
261 | 0, // 81 0x51 QZ_KP_EQUALS | |
262 | 82, // 82 0x52 0x52 KP 0 QZ_KP0 | |
263 | 79, // 83 0x53 0x4f KP 1 QZ_KP1 | |
264 | 80, // 84 0x54 0x50 KP 2 QZ_KP2 | |
265 | 81, // 85 0x55 0x51 KP 3 QZ_KP3 | |
266 | 75, // 86 0x56 0x4b KP 4 QZ_KP4 | |
267 | 76, // 87 0x57 0x4c KP 5 QZ_KP5 | |
268 | 77, // 88 0x58 0x4d KP 6 QZ_KP6 | |
269 | 71, // 89 0x59 0x47 KP 7 QZ_KP7 | |
270 | 0, // 90 0x5A Undefined | |
271 | 72, // 91 0x5B 0x48 KP 8 QZ_KP8 | |
272 | 73, // 92 0x5C 0x49 KP 9 QZ_KP9 | |
273 | 0, // 93 0x5D Undefined | |
274 | 0, // 94 0x5E Undefined | |
275 | 0, // 95 0x5F Undefined | |
276 | 63, // 96 0x60 0x3f F5 QZ_F5 | |
277 | 64, // 97 0x61 0x40 F6 QZ_F6 | |
278 | 65, // 98 0x62 0x41 F7 QZ_F7 | |
279 | 61, // 99 0x63 0x3d F3 QZ_F3 | |
280 | 66, // 100 0x64 0x42 F8 QZ_F8 | |
281 | 67, // 101 0x65 0x43 F9 QZ_F9 | |
282 | 0, // 102 0x66 Undefined | |
283 | 87, // 103 0x67 0x57 F11 QZ_F11 | |
284 | 0, // 104 0x68 Undefined | |
285 | 183,// 105 0x69 0xb7 QZ_PRINT | |
286 | 0, // 106 0x6A Undefined | |
287 | 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK | |
288 | 0, // 108 0x6C Undefined | |
289 | 68, // 109 0x6D 0x44 F10 QZ_F10 | |
290 | 0, // 110 0x6E Undefined | |
291 | 88, // 111 0x6F 0x58 F12 QZ_F12 | |
292 | 0, // 112 0x70 Undefined | |
293 | 110,// 113 0x71 0x0 QZ_PAUSE | |
294 | 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT | |
295 | 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME | |
296 | 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP | |
297 | 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE | |
298 | 62, // 118 0x76 0x3e F4 QZ_F4 | |
299 | 207,// 119 0x77 0xcf E0,4f END QZ_END | |
300 | 60, // 120 0x78 0x3c F2 QZ_F2 | |
301 | 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN | |
302 | 59, // 122 0x7A 0x3b F1 QZ_F1 | |
303 | 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT | |
304 | 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT | |
305 | 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN | |
306 | 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP | |
307 | /* 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 */ | |
308 | ||
309 | /* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */ | |
310 | /* | |
311 | 219 // 0xdb e0,5b L GUI | |
312 | 220 // 0xdc e0,5c R GUI | |
313 | 221 // 0xdd e0,5d APPS | |
314 | // E0,2A,E0,37 PRNT SCRN | |
315 | // E1,1D,45,E1,9D,C5 PAUSE | |
316 | 83 // 0x53 0x53 KP . | |
317 | // ACPI Scan Codes | |
318 | 222 // 0xde E0, 5E Power | |
319 | 223 // 0xdf E0, 5F Sleep | |
320 | 227 // 0xe3 E0, 63 Wake | |
321 | // Windows Multimedia Scan Codes | |
322 | 153 // 0x99 E0, 19 Next Track | |
323 | 144 // 0x90 E0, 10 Previous Track | |
324 | 164 // 0xa4 E0, 24 Stop | |
325 | 162 // 0xa2 E0, 22 Play/Pause | |
326 | 160 // 0xa0 E0, 20 Mute | |
327 | 176 // 0xb0 E0, 30 Volume Up | |
328 | 174 // 0xae E0, 2E Volume Down | |
329 | 237 // 0xed E0, 6D Media Select | |
330 | 236 // 0xec E0, 6C E-Mail | |
331 | 161 // 0xa1 E0, 21 Calculator | |
332 | 235 // 0xeb E0, 6B My Computer | |
333 | 229 // 0xe5 E0, 65 WWW Search | |
334 | 178 // 0xb2 E0, 32 WWW Home | |
335 | 234 // 0xea E0, 6A WWW Back | |
336 | 233 // 0xe9 E0, 69 WWW Forward | |
337 | 232 // 0xe8 E0, 68 WWW Stop | |
338 | 231 // 0xe7 E0, 67 WWW Refresh | |
339 | 230 // 0xe6 E0, 66 WWW Favorites | |
340 | */ | |
5b0753e0 FB |
341 | }; |
342 | ||
87f48e6a | 343 | int cocoa_keycode_to_qemu(int keycode) |
5b0753e0 | 344 | { |
87f48e6a | 345 | if((sizeof(keymap)/sizeof(int)) <= keycode) |
5b0753e0 FB |
346 | { |
347 | printf("(cocoa) warning unknow keycode 0x%x\n", keycode); | |
348 | return 0; | |
349 | } | |
350 | return keymap[keycode]; | |
351 | } | |
352 | ||
353 | /* | |
354 | ------------------------------------------------------ | |
355 | cocoa_refresh | |
356 | ------------------------------------------------------ | |
357 | */ | |
358 | static void cocoa_refresh(DisplayState *ds) | |
359 | { | |
360 | //printf("cocoa_refresh \n"); | |
361 | NSDate *distantPast; | |
362 | NSEvent *event; | |
363 | NSAutoreleasePool *pool; | |
5b0753e0 FB |
364 | |
365 | pool = [ [ NSAutoreleasePool alloc ] init ]; | |
366 | distantPast = [ NSDate distantPast ]; | |
367 | ||
368 | if (is_active_console(vga_console)) | |
369 | vga_update_display(); | |
370 | do { | |
371 | event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast | |
372 | inMode: NSDefaultRunLoopMode dequeue:YES ]; | |
373 | if (event != nil) { | |
374 | switch ([event type]) { | |
87f48e6a | 375 | case NSFlagsChanged: |
5b0753e0 FB |
376 | { |
377 | int keycode = cocoa_keycode_to_qemu([event keyCode]); | |
7c206a75 FB |
378 | |
379 | if (keycode) | |
87f48e6a | 380 | { |
7c206a75 FB |
381 | if (keycode == 58 || keycode == 69) { |
382 | /* emulate caps lock and num lock keydown and keyup */ | |
383 | kbd_put_keycode(keycode); | |
384 | kbd_put_keycode(keycode | 0x80); | |
385 | } else if (is_active_console(vga_console)) { | |
386 | if (keycode & 0x80) | |
387 | kbd_put_keycode(0xe0); | |
388 | if (modifiers_state[keycode] == 0) { | |
389 | /* keydown */ | |
390 | kbd_put_keycode(keycode & 0x7f); | |
391 | modifiers_state[keycode] = 1; | |
392 | } else { | |
393 | /* keyup */ | |
394 | kbd_put_keycode(keycode | 0x80); | |
395 | modifiers_state[keycode] = 0; | |
396 | } | |
397 | } | |
87f48e6a | 398 | } |
7c206a75 | 399 | |
87f48e6a FB |
400 | /* release Mouse grab when pressing ctrl+alt */ |
401 | if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) | |
7c206a75 | 402 | { |
87f48e6a FB |
403 | [window setTitle: @"QEMU"]; |
404 | [NSCursor unhide]; | |
405 | CGAssociateMouseAndMouseCursorPosition ( TRUE ); | |
406 | grab = 0; | |
7c206a75 | 407 | } |
5b0753e0 FB |
408 | } |
409 | break; | |
7c206a75 | 410 | |
87f48e6a FB |
411 | case NSKeyDown: |
412 | { | |
413 | int keycode = cocoa_keycode_to_qemu([event keyCode]); | |
414 | ||
415 | /* handle command Key Combos */ | |
416 | if ([event modifierFlags] & NSCommandKeyMask) { | |
417 | switch ([event keyCode]) { | |
418 | /* quit */ | |
419 | case 12: /* q key */ | |
420 | /* switch to windowed View */ | |
421 | exit(0); | |
422 | return; | |
423 | } | |
424 | } | |
425 | ||
426 | /* handle control + alt Key Combos */ | |
427 | if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { | |
428 | switch (keycode) { | |
429 | /* toggle Monitor */ | |
430 | case 0x02 ... 0x0a: /* '1' to '9' keys */ | |
431 | console_select(keycode - 0x02); | |
432 | if (is_active_console(vga_console)) { | |
433 | /* tell the vga console to redisplay itself */ | |
434 | vga_invalidate_display(); | |
435 | break; | |
436 | } | |
437 | } | |
438 | } else { | |
439 | /* handle standard key events */ | |
440 | if (is_active_console(vga_console)) { | |
441 | if (keycode & 0x80) //check bit for e0 in front | |
442 | kbd_put_keycode(0xe0); | |
443 | kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front | |
444 | /* handle monitor key events */ | |
445 | } else { | |
446 | switch([event keyCode]) { | |
447 | case 123: | |
448 | kbd_put_keysym(QEMU_KEY_LEFT); | |
449 | break; | |
450 | case 124: | |
451 | kbd_put_keysym(QEMU_KEY_RIGHT); | |
452 | break; | |
453 | case 125: | |
454 | kbd_put_keysym(QEMU_KEY_DOWN); | |
455 | break; | |
456 | case 126: | |
457 | kbd_put_keysym(QEMU_KEY_UP); | |
458 | break; | |
459 | default: | |
460 | kbd_put_keysym([[event characters] characterAtIndex:0]); | |
461 | break; | |
462 | } | |
463 | } | |
464 | } | |
465 | } | |
466 | break; | |
467 | ||
5b0753e0 | 468 | case NSKeyUp: |
5b0753e0 | 469 | { |
87f48e6a FB |
470 | int keycode = cocoa_keycode_to_qemu([event keyCode]); |
471 | if (is_active_console(vga_console)) { | |
472 | if (keycode & 0x80) | |
473 | kbd_put_keycode(0xe0); | |
474 | kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key | |
475 | } | |
5b0753e0 FB |
476 | } |
477 | break; | |
87f48e6a FB |
478 | |
479 | case NSMouseMoved: | |
480 | if (grab) { | |
481 | int dx = [event deltaX]; | |
482 | int dy = [event deltaY]; | |
483 | int dz = [event deltaZ]; | |
484 | int buttons = 0; | |
485 | kbd_mouse_event(dx, dy, dz, buttons); | |
486 | } | |
487 | break; | |
488 | ||
5b0753e0 | 489 | case NSLeftMouseDown: |
87f48e6a FB |
490 | if (grab) { |
491 | int buttons = 0; | |
492 | ||
493 | /* leftclick+command simulates rightclick */ | |
494 | if ([event modifierFlags] & NSCommandKeyMask) { | |
495 | buttons |= MOUSE_EVENT_RBUTTON; | |
496 | } else { | |
497 | buttons |= MOUSE_EVENT_LBUTTON; | |
498 | } | |
499 | kbd_mouse_event(0, 0, 0, buttons); | |
500 | } else { | |
501 | [NSApp sendEvent: event]; | |
502 | } | |
503 | break; | |
504 | ||
505 | case NSLeftMouseDragged: | |
506 | if (grab) { | |
507 | int dx = [event deltaX]; | |
508 | int dy = [event deltaY]; | |
509 | int dz = [event deltaZ]; | |
510 | int buttons = 0; | |
511 | if ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) { //leftclick+command simulates rightclick | |
512 | buttons |= MOUSE_EVENT_RBUTTON; | |
513 | } else { | |
514 | buttons |= MOUSE_EVENT_LBUTTON; | |
515 | } | |
516 | kbd_mouse_event(dx, dy, dz, buttons); | |
517 | } | |
518 | break; | |
519 | ||
5b0753e0 | 520 | case NSLeftMouseUp: |
87f48e6a FB |
521 | if (grab) { |
522 | kbd_mouse_event(0, 0, 0, 0); | |
523 | } else { | |
524 | [window setTitle: @"QEMU (Press ctrl + alt to release Mouse)"]; | |
525 | [NSCursor hide]; | |
526 | CGAssociateMouseAndMouseCursorPosition ( FALSE ); | |
527 | grab = 1; | |
528 | //[NSApp sendEvent: event]; | |
529 | } | |
530 | break; | |
531 | ||
5b0753e0 | 532 | case NSRightMouseDown: |
87f48e6a FB |
533 | if (grab) { |
534 | int buttons = 0; | |
535 | ||
536 | buttons |= MOUSE_EVENT_RBUTTON; | |
537 | kbd_mouse_event(0, 0, 0, buttons); | |
538 | } else { | |
539 | [NSApp sendEvent: event]; | |
540 | } | |
541 | break; | |
542 | ||
543 | case NSRightMouseDragged: | |
544 | if (grab) { | |
545 | int dx = [event deltaX]; | |
546 | int dy = [event deltaY]; | |
547 | int dz = [event deltaZ]; | |
548 | int buttons = 0; | |
549 | buttons |= MOUSE_EVENT_RBUTTON; | |
550 | kbd_mouse_event(dx, dy, dz, buttons); | |
551 | } | |
552 | break; | |
553 | ||
5b0753e0 | 554 | case NSRightMouseUp: |
87f48e6a FB |
555 | if (grab) { |
556 | kbd_mouse_event(0, 0, 0, 0); | |
557 | } else { | |
558 | [NSApp sendEvent: event]; | |
559 | } | |
560 | break; | |
561 | ||
5b0753e0 | 562 | case NSOtherMouseDragged: |
87f48e6a FB |
563 | if (grab) { |
564 | int dx = [event deltaX]; | |
565 | int dy = [event deltaY]; | |
566 | int dz = [event deltaZ]; | |
567 | int buttons = 0; | |
568 | buttons |= MOUSE_EVENT_MBUTTON; | |
569 | kbd_mouse_event(dx, dy, dz, buttons); | |
570 | } | |
571 | break; | |
572 | ||
573 | case NSOtherMouseDown: | |
574 | if (grab) { | |
575 | int buttons = 0; | |
576 | buttons |= MOUSE_EVENT_MBUTTON; | |
577 | kbd_mouse_event(0, 0, 0, buttons); | |
578 | } else { | |
579 | [NSApp sendEvent:event]; | |
580 | } | |
581 | break; | |
582 | ||
583 | case NSOtherMouseUp: | |
584 | if (grab) { | |
585 | kbd_mouse_event(0, 0, 0, 0); | |
586 | } else { | |
587 | [NSApp sendEvent: event]; | |
588 | } | |
589 | break; | |
590 | ||
591 | case NSScrollWheel: | |
592 | if (grab) { | |
593 | int dz = [event deltaY]; | |
594 | kbd_mouse_event(0, 0, -dz, 0); | |
595 | } | |
596 | break; | |
5b0753e0 FB |
597 | |
598 | default: [NSApp sendEvent:event]; | |
599 | } | |
600 | } | |
601 | } while(event != nil); | |
602 | } | |
603 | ||
604 | /* | |
605 | ------------------------------------------------------ | |
606 | cocoa_cleanup | |
607 | ------------------------------------------------------ | |
608 | */ | |
609 | ||
610 | static void cocoa_cleanup(void) | |
611 | { | |
612 | ||
613 | } | |
614 | ||
615 | /* | |
616 | ------------------------------------------------------ | |
617 | cocoa_display_init | |
618 | ------------------------------------------------------ | |
619 | */ | |
620 | ||
621 | void cocoa_display_init(DisplayState *ds, int full_screen) | |
622 | { | |
623 | ds->dpy_update = cocoa_update; | |
624 | ds->dpy_resize = cocoa_resize; | |
625 | ds->dpy_refresh = cocoa_refresh; | |
626 | ||
627 | cocoa_resize(ds, 640, 400); | |
628 | ||
629 | atexit(cocoa_cleanup); | |
630 | } | |
631 | ||
632 | /* | |
633 | ------------------------------------------------------ | |
634 | Interface with Cocoa | |
635 | ------------------------------------------------------ | |
636 | */ | |
637 | ||
638 | ||
639 | /* | |
640 | ------------------------------------------------------ | |
641 | QemuWindow | |
642 | Some trick from SDL to use miniwindow | |
643 | ------------------------------------------------------ | |
644 | */ | |
645 | static void QZ_SetPortAlphaOpaque () | |
646 | { | |
647 | /* Assume 32 bit if( bpp == 32 )*/ | |
648 | if ( 1 ) { | |
649 | ||
650 | uint32_t *pixels = (uint32_t*) current_ds.data; | |
651 | uint32_t rowPixels = current_ds.linesize / 4; | |
652 | uint32_t i, j; | |
653 | ||
654 | for (i = 0; i < current_ds.height; i++) | |
655 | for (j = 0; j < current_ds.width; j++) { | |
656 | ||
657 | pixels[ (i * rowPixels) + j ] |= 0xFF000000; | |
658 | } | |
659 | } | |
660 | } | |
661 | ||
662 | @implementation QemuWindow | |
663 | - (void)miniaturize:(id)sender | |
664 | { | |
665 | ||
666 | /* make the alpha channel opaque so anim won't have holes in it */ | |
667 | QZ_SetPortAlphaOpaque (); | |
668 | ||
669 | [ super miniaturize:sender ]; | |
670 | ||
671 | } | |
672 | - (void)display | |
673 | { | |
674 | /* | |
675 | This method fires just before the window deminaturizes from the Dock. | |
676 | ||
677 | We'll save the current visible surface, let the window manager redraw any | |
678 | UI elements, and restore the SDL surface. This way, no expose event | |
679 | is required, and the deminiaturize works perfectly. | |
680 | */ | |
681 | ||
682 | /* make sure pixels are fully opaque */ | |
683 | QZ_SetPortAlphaOpaque (); | |
684 | ||
685 | /* save current visible SDL surface */ | |
686 | [ self cacheImageInRect:[ qd_view frame ] ]; | |
687 | ||
688 | /* let the window manager redraw controls, border, etc */ | |
689 | [ super display ]; | |
690 | ||
691 | /* restore visible SDL surface */ | |
692 | [ self restoreCachedImage ]; | |
693 | } | |
694 | ||
695 | @end | |
696 | ||
697 | ||
698 | /* | |
699 | ------------------------------------------------------ | |
700 | QemuCocoaGUIController | |
701 | NSApp's delegate - indeed main object | |
702 | ------------------------------------------------------ | |
703 | */ | |
704 | ||
705 | @interface QemuCocoaGUIController : NSObject | |
706 | { | |
707 | } | |
708 | - (void)applicationDidFinishLaunching: (NSNotification *) note; | |
709 | - (void)applicationWillTerminate:(NSNotification *)aNotification; | |
710 | ||
711 | - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; | |
712 | ||
713 | - (void)startEmulationWithArgc:(int)argc argv:(char**)argv; | |
714 | @end | |
715 | ||
716 | @implementation QemuCocoaGUIController | |
717 | /* Called when the internal event loop has just started running */ | |
718 | - (void)applicationDidFinishLaunching: (NSNotification *) note | |
719 | { | |
5a246934 FB |
720 | |
721 | /* Display an open dialog box if no argument were passed or | |
722 | if qemu was launched from the finder ( the Finder passes "-psn" ) */ | |
723 | ||
724 | if( gArgc <= 1 || strncmp (gArgv[1], "-psn", 4) == 0) | |
5b0753e0 FB |
725 | { |
726 | NSOpenPanel *op = [[NSOpenPanel alloc] init]; | |
727 | ||
728 | cocoa_resize(¤t_ds, 640, 400); | |
729 | ||
730 | [op setPrompt:@"Boot image"]; | |
731 | ||
732 | [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"]; | |
733 | ||
7a674b13 | 734 | [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil] |
5b0753e0 FB |
735 | modalForWindow:window modalDelegate:self |
736 | didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; | |
737 | } | |
5a246934 FB |
738 | else |
739 | { | |
740 | /* or Launch Qemu, with the global args */ | |
741 | [self startEmulationWithArgc:gArgc argv:gArgv]; | |
742 | } | |
5b0753e0 FB |
743 | } |
744 | ||
745 | - (void)applicationWillTerminate:(NSNotification *)aNotification | |
746 | { | |
747 | printf("Application will terminate\n"); | |
748 | qemu_system_shutdown_request(); | |
749 | /* In order to avoid a crash */ | |
750 | exit(0); | |
751 | } | |
752 | ||
753 | - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo | |
754 | { | |
755 | if(returnCode == NSCancelButton) | |
756 | { | |
757 | exit(0); | |
758 | } | |
759 | ||
760 | if(returnCode == NSOKButton) | |
761 | { | |
762 | char *bin = "qemu"; | |
763 | char *img = (char*)[ [ sheet filename ] cString]; | |
764 | ||
765 | char **argv = (char**)malloc( sizeof(char*)*3 ); | |
766 | ||
767 | asprintf(&argv[0], "%s", bin); | |
768 | asprintf(&argv[1], "-hda"); | |
769 | asprintf(&argv[2], "%s", img); | |
770 | ||
771 | printf("Using argc %d argv %s -hda %s\n", 3, bin, img); | |
772 | ||
773 | [self startEmulationWithArgc:3 argv:(char**)argv]; | |
774 | } | |
775 | } | |
776 | ||
777 | - (void)startEmulationWithArgc:(int)argc argv:(char**)argv | |
778 | { | |
779 | int status; | |
780 | /* Launch Qemu */ | |
781 | printf("starting qemu...\n"); | |
782 | status = qemu_main (argc, argv); | |
783 | exit(status); | |
784 | } | |
785 | @end | |
786 | ||
787 | /* | |
788 | ------------------------------------------------------ | |
789 | Application Creation | |
790 | ------------------------------------------------------ | |
791 | */ | |
792 | ||
793 | /* Dock Connection */ | |
794 | typedef struct CPSProcessSerNum | |
795 | { | |
796 | UInt32 lo; | |
797 | UInt32 hi; | |
798 | } CPSProcessSerNum; | |
799 | ||
800 | extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); | |
801 | extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); | |
802 | extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); | |
803 | ||
804 | /* Menu Creation */ | |
805 | static void setApplicationMenu(void) | |
806 | { | |
807 | /* warning: this code is very odd */ | |
808 | NSMenu *appleMenu; | |
809 | NSMenuItem *menuItem; | |
810 | NSString *title; | |
811 | NSString *appName; | |
812 | ||
813 | appName = @"Qemu"; | |
814 | appleMenu = [[NSMenu alloc] initWithTitle:@""]; | |
815 | ||
816 | /* Add menu items */ | |
817 | title = [@"About " stringByAppendingString:appName]; | |
818 | [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; | |
819 | ||
820 | [appleMenu addItem:[NSMenuItem separatorItem]]; | |
821 | ||
822 | title = [@"Hide " stringByAppendingString:appName]; | |
823 | [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; | |
824 | ||
825 | menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; | |
826 | [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; | |
827 | ||
828 | [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; | |
829 | ||
830 | [appleMenu addItem:[NSMenuItem separatorItem]]; | |
831 | ||
832 | title = [@"Quit " stringByAppendingString:appName]; | |
833 | [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; | |
834 | ||
835 | ||
836 | /* Put menu into the menubar */ | |
837 | menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; | |
838 | [menuItem setSubmenu:appleMenu]; | |
839 | [[NSApp mainMenu] addItem:menuItem]; | |
840 | ||
841 | /* Tell the application object that this is now the application menu */ | |
842 | [NSApp setAppleMenu:appleMenu]; | |
843 | ||
844 | /* Finally give up our references to the objects */ | |
845 | [appleMenu release]; | |
846 | [menuItem release]; | |
847 | } | |
848 | ||
849 | /* Create a window menu */ | |
850 | static void setupWindowMenu(void) | |
851 | { | |
852 | NSMenu *windowMenu; | |
853 | NSMenuItem *windowMenuItem; | |
854 | NSMenuItem *menuItem; | |
855 | ||
856 | windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; | |
857 | ||
858 | /* "Minimize" item */ | |
859 | menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; | |
860 | [windowMenu addItem:menuItem]; | |
861 | [menuItem release]; | |
862 | ||
863 | /* Put menu into the menubar */ | |
864 | windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; | |
865 | [windowMenuItem setSubmenu:windowMenu]; | |
866 | [[NSApp mainMenu] addItem:windowMenuItem]; | |
867 | ||
868 | /* Tell the application object that this is now the window menu */ | |
869 | [NSApp setWindowsMenu:windowMenu]; | |
870 | ||
871 | /* Finally give up our references to the objects */ | |
872 | [windowMenu release]; | |
873 | [windowMenuItem release]; | |
874 | ||
875 | } | |
876 | ||
877 | static void CustomApplicationMain (argc, argv) | |
878 | { | |
879 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |
880 | QemuCocoaGUIController *gui_controller; | |
881 | CPSProcessSerNum PSN; | |
882 | ||
883 | [NSApplication sharedApplication]; | |
884 | ||
885 | if (!CPSGetCurrentProcess(&PSN)) | |
886 | if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) | |
887 | if (!CPSSetFrontProcess(&PSN)) | |
888 | [NSApplication sharedApplication]; | |
889 | ||
890 | /* Set up the menubar */ | |
891 | [NSApp setMainMenu:[[NSMenu alloc] init]]; | |
892 | setApplicationMenu(); | |
893 | setupWindowMenu(); | |
894 | ||
895 | /* Create SDLMain and make it the app delegate */ | |
896 | gui_controller = [[QemuCocoaGUIController alloc] init]; | |
897 | [NSApp setDelegate:gui_controller]; | |
898 | ||
899 | /* Start the main event loop */ | |
900 | [NSApp run]; | |
901 | ||
902 | [gui_controller release]; | |
903 | [pool release]; | |
904 | } | |
905 | ||
906 | /* Real main of qemu-cocoa */ | |
907 | int main(int argc, char **argv) | |
908 | { | |
909 | gArgc = argc; | |
910 | gArgv = argv; | |
911 | ||
912 | CustomApplicationMain (argc, argv); | |
913 | ||
914 | return 0; | |
915 | } |