root / cocoa.m @ 3b46e624
History | View | Annotate | Download (33 kB)
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 |
*/ |
37 |
|
38 |
#import <Cocoa/Cocoa.h> |
39 |
|
40 |
#include "vl.h" |
41 |
|
42 |
NSWindow *window = NULL; |
43 |
NSQuickDrawView *qd_view = NULL; |
44 |
|
45 |
|
46 |
int gArgc; |
47 |
char **gArgv; |
48 |
DisplayState current_ds; |
49 |
|
50 |
int grab = 0; |
51 |
int modifiers_state[256]; |
52 |
|
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 |
#ifdef __LITTLE_ENDIAN__ |
168 |
ds->bgr = 1; |
169 |
#else |
170 |
ds->bgr = 0; |
171 |
#endif |
172 |
|
173 |
current_ds = *ds; |
174 |
} |
175 |
|
176 |
/* |
177 |
------------------------------------------------------ |
178 |
keymap conversion |
179 |
------------------------------------------------------ |
180 |
*/ |
181 |
|
182 |
int keymap[] = |
183 |
{ |
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 */ |
313 |
|
314 |
/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */ |
315 |
/* |
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 |
333 |
174 // 0xae E0, 2E Volume Down |
334 |
237 // 0xed E0, 6D Media Select |
335 |
236 // 0xec E0, 6C E-Mail |
336 |
161 // 0xa1 E0, 21 Calculator |
337 |
235 // 0xeb E0, 6B My Computer |
338 |
229 // 0xe5 E0, 65 WWW Search |
339 |
178 // 0xb2 E0, 32 WWW Home |
340 |
234 // 0xea E0, 6A WWW Back |
341 |
233 // 0xe9 E0, 69 WWW Forward |
342 |
232 // 0xe8 E0, 68 WWW Stop |
343 |
231 // 0xe7 E0, 67 WWW Refresh |
344 |
230 // 0xe6 E0, 66 WWW Favorites |
345 |
*/ |
346 |
}; |
347 |
|
348 |
int cocoa_keycode_to_qemu(int keycode) |
349 |
{ |
350 |
if((sizeof(keymap)/sizeof(int)) <= keycode) |
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; |
369 |
|
370 |
pool = [ [ NSAutoreleasePool alloc ] init ]; |
371 |
distantPast = [ NSDate distantPast ]; |
372 |
|
373 |
vga_hw_update(); |
374 |
|
375 |
do { |
376 |
event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast |
377 |
inMode: NSDefaultRunLoopMode dequeue:YES ]; |
378 |
if (event != nil) { |
379 |
switch ([event type]) { |
380 |
case NSFlagsChanged: |
381 |
{ |
382 |
int keycode = cocoa_keycode_to_qemu([event keyCode]); |
383 |
|
384 |
if (keycode) |
385 |
{ |
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); |
390 |
} else if (is_graphic_console()) { |
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 |
} |
403 |
} |
404 |
|
405 |
/* release Mouse grab when pressing ctrl+alt */ |
406 |
if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) |
407 |
{ |
408 |
[window setTitle: @"QEMU"]; |
409 |
[NSCursor unhide]; |
410 |
CGAssociateMouseAndMouseCursorPosition ( TRUE ); |
411 |
grab = 0; |
412 |
} |
413 |
} |
414 |
break; |
415 |
|
416 |
case NSKeyDown: |
417 |
{ |
418 |
int keycode = cocoa_keycode_to_qemu([event keyCode]); |
419 |
|
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 |
} |
430 |
|
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); |
437 |
break; |
438 |
} |
439 |
} else { |
440 |
/* handle standard key events */ |
441 |
if (is_graphic_console()) { |
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 { |
447 |
int keysym = 0; |
448 |
|
449 |
switch([event keyCode]) { |
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 |
} |
478 |
} |
479 |
if (keysym) |
480 |
kbd_put_keysym(keysym); |
481 |
} |
482 |
} |
483 |
} |
484 |
break; |
485 |
|
486 |
case NSKeyUp: |
487 |
{ |
488 |
int keycode = cocoa_keycode_to_qemu([event keyCode]); |
489 |
if (is_graphic_console()) { |
490 |
if (keycode & 0x80) |
491 |
kbd_put_keycode(0xe0); |
492 |
kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key |
493 |
} |
494 |
} |
495 |
break; |
496 |
|
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; |
506 |
|
507 |
case NSLeftMouseDown: |
508 |
if (grab) { |
509 |
int buttons = 0; |
510 |
|
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; |
522 |
|
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; |
537 |
|
538 |
case NSLeftMouseUp: |
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; |
549 |
|
550 |
case NSRightMouseDown: |
551 |
if (grab) { |
552 |
int buttons = 0; |
553 |
|
554 |
buttons |= MOUSE_EVENT_RBUTTON; |
555 |
kbd_mouse_event(0, 0, 0, buttons); |
556 |
} else { |
557 |
[NSApp sendEvent: event]; |
558 |
} |
559 |
break; |
560 |
|
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; |
571 |
|
572 |
case NSRightMouseUp: |
573 |
if (grab) { |
574 |
kbd_mouse_event(0, 0, 0, 0); |
575 |
} else { |
576 |
[NSApp sendEvent: event]; |
577 |
} |
578 |
break; |
579 |
|
580 |
case NSOtherMouseDragged: |
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; |
590 |
|
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; |
600 |
|
601 |
case NSOtherMouseUp: |
602 |
if (grab) { |
603 |
kbd_mouse_event(0, 0, 0, 0); |
604 |
} else { |
605 |
[NSApp sendEvent: event]; |
606 |
} |
607 |
break; |
608 |
|
609 |
case NSScrollWheel: |
610 |
if (grab) { |
611 |
int dz = [event deltaY]; |
612 |
kbd_mouse_event(0, 0, -dz, 0); |
613 |
} |
614 |
break; |
615 |
|
616 |
default: [NSApp sendEvent:event]; |
617 |
} |
618 |
} |
619 |
} while(event != nil); |
620 |
} |
621 |
|
622 |
/* |
623 |
------------------------------------------------------ |
624 |
cocoa_cleanup |
625 |
------------------------------------------------------ |
626 |
*/ |
627 |
|
628 |
static void cocoa_cleanup(void) |
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; |
644 |
|
645 |
cocoa_resize(ds, 640, 400); |
646 |
|
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 () |
664 |
{ |
665 |
/* Assume 32 bit if( bpp == 32 )*/ |
666 |
if ( 1 ) { |
667 |
|
668 |
uint32_t *pixels = (uint32_t*) current_ds.data; |
669 |
uint32_t rowPixels = current_ds.linesize / 4; |
670 |
uint32_t i, j; |
671 |
|
672 |
for (i = 0; i < current_ds.height; i++) |
673 |
for (j = 0; j < current_ds.width; j++) { |
674 |
|
675 |
pixels[ (i * rowPixels) + j ] |= 0xFF000000; |
676 |
} |
677 |
} |
678 |
} |
679 |
|
680 |
@implementation QemuWindow |
681 |
- (void)miniaturize:(id)sender |
682 |
{ |
683 |
|
684 |
/* make the alpha channel opaque so anim won't have holes in it */ |
685 |
QZ_SetPortAlphaOpaque (); |
686 |
|
687 |
[ super miniaturize:sender ]; |
688 |
|
689 |
} |
690 |
- (void)display |
691 |
{ |
692 |
/* |
693 |
This method fires just before the window deminaturizes from the Dock. |
694 |
|
695 |
We'll save the current visible surface, let the window manager redraw any |
696 |
UI elements, and restore the SDL surface. This way, no expose event |
697 |
is required, and the deminiaturize works perfectly. |
698 |
*/ |
699 |
|
700 |
/* make sure pixels are fully opaque */ |
701 |
QZ_SetPortAlphaOpaque (); |
702 |
|
703 |
/* save current visible SDL surface */ |
704 |
[ self cacheImageInRect:[ qd_view frame ] ]; |
705 |
|
706 |
/* let the window manager redraw controls, border, etc */ |
707 |
[ super display ]; |
708 |
|
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 |
{ |
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) |
743 |
{ |
744 |
NSOpenPanel *op = [[NSOpenPanel alloc] init]; |
745 |
|
746 |
cocoa_resize(¤t_ds, 640, 400); |
747 |
|
748 |
[op setPrompt:@"Boot image"]; |
749 |
|
750 |
[op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"]; |
751 |
|
752 |
[op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil] |
753 |
modalForWindow:window modalDelegate:self |
754 |
didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; |
755 |
} |
756 |
else |
757 |
{ |
758 |
/* or Launch Qemu, with the global args */ |
759 |
[self startEmulationWithArgc:gArgc argv:gArgv]; |
760 |
} |
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 |
} |
777 |
|
778 |
if(returnCode == NSOKButton) |
779 |
{ |
780 |
char *bin = "qemu"; |
781 |
char *img = (char*)[ [ sheet filename ] cString]; |
782 |
|
783 |
char **argv = (char**)malloc( sizeof(char*)*3 ); |
784 |
|
785 |
asprintf(&argv[0], "%s", bin); |
786 |
asprintf(&argv[1], "-hda"); |
787 |
asprintf(&argv[2], "%s", img); |
788 |
|
789 |
printf("Using argc %d argv %s -hda %s\n", 3, bin, img); |
790 |
|
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; |
830 |
|
831 |
appName = @"Qemu"; |
832 |
appleMenu = [[NSMenu alloc] initWithTitle:@""]; |
833 |
|
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 |
|
853 |
|
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"]; |
875 |
|
876 |
/* "Minimize" item */ |
877 |
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; |
878 |
[windowMenu addItem:menuItem]; |
879 |
[menuItem release]; |
880 |
|
881 |
/* Put menu into the menubar */ |
882 |
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; |
883 |
[windowMenuItem setSubmenu:windowMenu]; |
884 |
[[NSApp mainMenu] addItem:windowMenuItem]; |
885 |
|
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]; |
892 |
} |
893 |
|
894 |
static void CustomApplicationMain(void) |
895 |
{ |
896 |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
897 |
QemuCocoaGUIController *gui_controller; |
898 |
CPSProcessSerNum PSN; |
899 |
|
900 |
[NSApplication sharedApplication]; |
901 |
|
902 |
if (!CPSGetCurrentProcess(&PSN)) |
903 |
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) |
904 |
if (!CPSSetFrontProcess(&PSN)) |
905 |
[NSApplication sharedApplication]; |
906 |
|
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]; |
915 |
|
916 |
/* Start the main event loop */ |
917 |
[NSApp run]; |
918 |
|
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; |
928 |
|
929 |
CustomApplicationMain(); |
930 |
|
931 |
return 0; |
932 |
} |