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