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