nkeynes@681 | 1 | /**
|
nkeynes@681 | 2 | * $Id$
|
nkeynes@681 | 3 | *
|
nkeynes@681 | 4 | * The OS/X side of the video support (responsible for actually displaying /
|
nkeynes@681 | 5 | * rendering frames)
|
nkeynes@681 | 6 | *
|
nkeynes@681 | 7 | * Copyright (c) 2008 Nathan Keynes.
|
nkeynes@681 | 8 | *
|
nkeynes@681 | 9 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@681 | 10 | * it under the terms of the GNU General Public License as published by
|
nkeynes@681 | 11 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@681 | 12 | * (at your option) any later version.
|
nkeynes@681 | 13 | *
|
nkeynes@681 | 14 | * This program is distributed in the hope that it will be useful,
|
nkeynes@681 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@681 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@681 | 17 | * GNU General Public License for more details.
|
nkeynes@681 | 18 | */
|
nkeynes@681 | 19 |
|
nkeynes@681 | 20 | #include <stdlib.h>
|
nkeynes@681 | 21 | #include <string.h>
|
nkeynes@681 | 22 | #include "lxdream.h"
|
nkeynes@681 | 23 | #include "display.h"
|
nkeynes@681 | 24 | #include "dckeysyms.h"
|
nkeynes@681 | 25 | #include "cocoaui/cocoaui.h"
|
nkeynes@681 | 26 | #include "drivers/video_nsgl.h"
|
nkeynes@681 | 27 | #include "drivers/video_gl.h"
|
nkeynes@681 | 28 | #include "pvr2/pvr2.h"
|
nkeynes@681 | 29 | #import <AppKit/AppKit.h>
|
nkeynes@681 | 30 |
|
nkeynes@681 | 31 | #include "mac_keymap.h"
|
nkeynes@681 | 32 |
|
nkeynes@781 | 33 | #define MOUSE_X_SCALE 5
|
nkeynes@781 | 34 | #define MOUSE_Y_SCALE 5
|
nkeynes@781 | 35 |
|
nkeynes@681 | 36 | static gboolean video_osx_init();
|
nkeynes@681 | 37 | static void video_osx_shutdown();
|
nkeynes@681 | 38 | static void video_osx_display_blank( uint32_t colour );
|
nkeynes@681 | 39 | static uint16_t video_osx_resolve_keysym( const gchar *keysym );
|
nkeynes@681 | 40 | static uint16_t video_osx_keycode_to_dckeysym(uint16_t keycode);
|
nkeynes@770 | 41 | static gchar *video_osx_keycode_to_keysym(uint16_t keycode);
|
nkeynes@681 | 42 |
|
nkeynes@700 | 43 | struct display_driver display_osx_driver = {
|
nkeynes@700 | 44 | "osx",
|
nkeynes@700 | 45 | N_("OS X Cocoa GUI-based OpenGL driver"),
|
nkeynes@700 | 46 | video_osx_init, video_osx_shutdown,
|
nkeynes@700 | 47 | video_osx_resolve_keysym,
|
nkeynes@700 | 48 | video_osx_keycode_to_dckeysym,
|
nkeynes@770 | 49 | video_osx_keycode_to_keysym,
|
nkeynes@805 | 50 | NULL, NULL, NULL, NULL, NULL,
|
nkeynes@805 | 51 | NULL,
|
nkeynes@700 | 52 | video_osx_display_blank, NULL };
|
nkeynes@681 | 53 |
|
nkeynes@681 | 54 |
|
nkeynes@681 | 55 | static NSView *video_view = NULL;
|
nkeynes@681 | 56 | int video_width = 640;
|
nkeynes@681 | 57 | int video_height = 480;
|
nkeynes@681 | 58 |
|
nkeynes@681 | 59 | #define MAX_MASK_KEYCODE 128
|
nkeynes@681 | 60 |
|
nkeynes@780 | 61 | @interface LxdreamOSXView : LxdreamVideoView
|
nkeynes@681 | 62 | {
|
nkeynes@681 | 63 | int flagsMask[MAX_MASK_KEYCODE];
|
nkeynes@681 | 64 | }
|
nkeynes@681 | 65 | @end
|
nkeynes@681 | 66 |
|
nkeynes@681 | 67 | @implementation LxdreamVideoView
|
nkeynes@780 | 68 | - (void)setIsGrabbed: (BOOL)grabbed
|
nkeynes@780 | 69 | {
|
nkeynes@780 | 70 | isGrabbed = grabbed;
|
nkeynes@780 | 71 | }
|
nkeynes@780 | 72 | - (void) setDelegate: (id)other
|
nkeynes@780 | 73 | {
|
nkeynes@780 | 74 | delegate = other;
|
nkeynes@780 | 75 | }
|
nkeynes@780 | 76 | - (id)delegate
|
nkeynes@780 | 77 | {
|
nkeynes@780 | 78 | return delegate;
|
nkeynes@780 | 79 | }
|
nkeynes@780 | 80 | @end
|
nkeynes@780 | 81 |
|
nkeynes@780 | 82 | @implementation LxdreamOSXView
|
nkeynes@681 | 83 | //--------------------------------------------------------------------
|
nkeynes@681 | 84 | - (id)initWithFrame: (NSRect)contentRect
|
nkeynes@681 | 85 | {
|
nkeynes@681 | 86 | if( [super initWithFrame: contentRect] != nil ) {
|
nkeynes@681 | 87 | int i;
|
nkeynes@681 | 88 | isGrabbed = NO;
|
nkeynes@681 | 89 | for( i=0; i<MAX_MASK_KEYCODE; i++ ) {
|
nkeynes@681 | 90 | flagsMask[i] = 0;
|
nkeynes@681 | 91 | }
|
nkeynes@681 | 92 | return self;
|
nkeynes@681 | 93 | }
|
nkeynes@681 | 94 | return nil;
|
nkeynes@681 | 95 | }
|
nkeynes@839 | 96 | - (BOOL)requestGrab
|
nkeynes@780 | 97 | {
|
nkeynes@780 | 98 | if( delegate && [delegate respondsToSelector: @selector(viewRequestedGrab:)] )
|
nkeynes@839 | 99 | return [delegate performSelector: @selector(viewRequestedGrab:) withObject: self] != nil;
|
nkeynes@839 | 100 | return NO;
|
nkeynes@780 | 101 | }
|
nkeynes@839 | 102 | - (BOOL)requestUngrab
|
nkeynes@780 | 103 | {
|
nkeynes@780 | 104 | if( delegate && [delegate respondsToSelector: @selector(viewRequestedUngrab:)] )
|
nkeynes@839 | 105 | return [delegate performSelector: @selector(viewRequestedUngrab:) withObject: self] != nil;
|
nkeynes@839 | 106 | return NO;
|
nkeynes@780 | 107 | }
|
nkeynes@681 | 108 | - (BOOL)isOpaque
|
nkeynes@681 | 109 | {
|
nkeynes@736 | 110 | return YES;
|
nkeynes@681 | 111 | }
|
nkeynes@681 | 112 | - (BOOL)acceptsFirstResponder
|
nkeynes@681 | 113 | {
|
nkeynes@736 | 114 | return YES;
|
nkeynes@681 | 115 | }
|
nkeynes@681 | 116 | - (BOOL)isFlipped
|
nkeynes@681 | 117 | {
|
nkeynes@736 | 118 | return YES;
|
nkeynes@681 | 119 | }
|
nkeynes@681 | 120 | //--------------------------------------------------------------------
|
nkeynes@681 | 121 | - (void)drawRect: (NSRect) rect
|
nkeynes@681 | 122 | {
|
nkeynes@681 | 123 | NSSize size = [self frame].size;
|
nkeynes@681 | 124 | if( video_width != size.width || video_height != size.height ) {
|
nkeynes@681 | 125 | video_width = size.width;
|
nkeynes@681 | 126 | video_height = size.height;
|
nkeynes@681 | 127 | video_nsgl_update();
|
nkeynes@681 | 128 | }
|
nkeynes@681 | 129 | pvr2_redraw_display();
|
nkeynes@681 | 130 | }
|
nkeynes@681 | 131 | - (void)keyDown: (NSEvent *) event
|
nkeynes@681 | 132 | {
|
nkeynes@681 | 133 | if( ![event isARepeat] ) {
|
nkeynes@681 | 134 | input_event_keydown( NULL, [event keyCode]+1, 1 );
|
nkeynes@681 | 135 | }
|
nkeynes@681 | 136 | }
|
nkeynes@681 | 137 | - (void)keyUp: (NSEvent *) event
|
nkeynes@681 | 138 | {
|
nkeynes@681 | 139 | input_event_keyup( NULL, [event keyCode]+1, 1 );
|
nkeynes@681 | 140 | }
|
nkeynes@681 | 141 | - (void)flagsChanged: (NSEvent *) event
|
nkeynes@681 | 142 | {
|
nkeynes@681 | 143 | int keycode = [event keyCode];
|
nkeynes@780 | 144 | if( ([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask) ) {
|
nkeynes@780 | 145 | [self requestUngrab];
|
nkeynes@681 | 146 | }
|
nkeynes@736 | 147 |
|
nkeynes@681 | 148 | if( flagsMask[keycode] == 0 ) {
|
nkeynes@681 | 149 | input_event_keydown( NULL, keycode+1, 1 );
|
nkeynes@681 | 150 | flagsMask[keycode] = 1;
|
nkeynes@681 | 151 | } else {
|
nkeynes@681 | 152 | input_event_keyup( NULL, keycode+1, 1 );
|
nkeynes@681 | 153 | flagsMask[keycode] = 0;
|
nkeynes@681 | 154 | }
|
nkeynes@681 | 155 | }
|
nkeynes@850 | 156 | - (void)emitMouseDownEvent: (NSEvent *)event button: (int)button
|
nkeynes@839 | 157 | {
|
nkeynes@839 | 158 | if( isGrabbed ) {
|
nkeynes@850 | 159 | input_event_mousedown( button, 0, 0, FALSE );
|
nkeynes@839 | 160 | } else {
|
nkeynes@839 | 161 | NSPoint pt = [event locationInWindow];
|
nkeynes@850 | 162 | input_event_mousedown( button, (int)(pt.x * DISPLAY_WIDTH) / video_width,
|
nkeynes@850 | 163 | DISPLAY_HEIGHT - ((int)(pt.y * DISPLAY_HEIGHT) / video_height), TRUE );
|
nkeynes@850 | 164 | }
|
nkeynes@850 | 165 | }
|
nkeynes@850 | 166 | - (void)emitMouseUpEvent: (NSEvent *)event button: (int)button
|
nkeynes@850 | 167 | {
|
nkeynes@850 | 168 | if( isGrabbed ) {
|
nkeynes@850 | 169 | input_event_mouseup( button, 0, 0, FALSE );
|
nkeynes@850 | 170 | } else {
|
nkeynes@850 | 171 | NSPoint pt = [event locationInWindow];
|
nkeynes@850 | 172 | input_event_mouseup( button, (int)(pt.x * DISPLAY_WIDTH) / video_width,
|
nkeynes@850 | 173 | DISPLAY_HEIGHT - ((int)(pt.y * DISPLAY_HEIGHT) / video_height), TRUE );
|
nkeynes@850 | 174 | }
|
nkeynes@850 | 175 | }
|
nkeynes@850 | 176 | - (void)emitMouseMoveEvent: (NSEvent *)event
|
nkeynes@850 | 177 | {
|
nkeynes@850 | 178 | if( isGrabbed ) {
|
nkeynes@850 | 179 | input_event_mousemove( [event deltaX] * MOUSE_X_SCALE, [event deltaY] * MOUSE_Y_SCALE, FALSE );
|
nkeynes@850 | 180 | } else {
|
nkeynes@850 | 181 | NSPoint pt = [event locationInWindow];
|
nkeynes@850 | 182 | input_event_mousemove( (int)(pt.x * DISPLAY_WIDTH) / video_width,
|
nkeynes@850 | 183 | DISPLAY_HEIGHT - ((int)(pt.y * DISPLAY_HEIGHT) / video_height), TRUE );
|
nkeynes@850 | 184 | }
|
nkeynes@850 | 185 | }
|
nkeynes@850 | 186 | - (void)mouseExited: (NSEvent *)event
|
nkeynes@850 | 187 | {
|
nkeynes@850 | 188 | if( !isGrabbed ) {
|
nkeynes@850 | 189 | input_event_mousemove( -1, -1, TRUE );
|
nkeynes@839 | 190 | }
|
nkeynes@839 | 191 | }
|
nkeynes@839 | 192 |
|
nkeynes@681 | 193 | - (void)mouseDown: (NSEvent *) event
|
nkeynes@681 | 194 | {
|
nkeynes@839 | 195 | // If using grab but not grabbed yet, the first click should be consumed
|
nkeynes@839 | 196 | // by the grabber. In all other circumstances we process normally.
|
nkeynes@839 | 197 | if( isGrabbed || ![self requestGrab] ) {
|
nkeynes@850 | 198 | [self emitMouseDownEvent: event button: 0];
|
nkeynes@681 | 199 | }
|
nkeynes@681 | 200 | }
|
nkeynes@681 | 201 | - (void)mouseUp: (NSEvent *)event
|
nkeynes@681 | 202 | {
|
nkeynes@850 | 203 | [self emitMouseUpEvent: event button: 0];
|
nkeynes@681 | 204 | }
|
nkeynes@681 | 205 |
|
nkeynes@681 | 206 | - (void)rightMouseDown: (NSEvent *) event
|
nkeynes@681 | 207 | {
|
nkeynes@850 | 208 | [self emitMouseDownEvent: event button: 1];
|
nkeynes@681 | 209 | }
|
nkeynes@681 | 210 | - (void)rightMouseUp: (NSEvent *)event
|
nkeynes@681 | 211 | {
|
nkeynes@850 | 212 | [self emitMouseUpEvent: event button: 1];
|
nkeynes@681 | 213 | }
|
nkeynes@681 | 214 | - (void)otherMouseDown: (NSEvent *) event
|
nkeynes@681 | 215 | {
|
nkeynes@850 | 216 | [self emitMouseDownEvent: event button: [event buttonNumber]];
|
nkeynes@681 | 217 | }
|
nkeynes@681 | 218 | - (void)otherMouseUp: (NSEvent *) event
|
nkeynes@681 | 219 | {
|
nkeynes@850 | 220 | [self emitMouseUpEvent: event button: [event buttonNumber]];
|
nkeynes@681 | 221 | }
|
nkeynes@681 | 222 | - (void)mouseMoved: (NSEvent *) event
|
nkeynes@681 | 223 | {
|
nkeynes@850 | 224 | [self emitMouseMoveEvent: event];
|
nkeynes@681 | 225 | }
|
nkeynes@681 | 226 | - (void)mouseDragged: (NSEvent *) event
|
nkeynes@681 | 227 | {
|
nkeynes@850 | 228 | [self emitMouseMoveEvent: event];
|
nkeynes@681 | 229 | }
|
nkeynes@681 | 230 | - (void)rightMouseDragged: (NSEvent *) event
|
nkeynes@681 | 231 | {
|
nkeynes@850 | 232 | [self emitMouseMoveEvent: event];
|
nkeynes@681 | 233 | }
|
nkeynes@681 | 234 | - (void)otherMouseDragged: (NSEvent *) event
|
nkeynes@681 | 235 | {
|
nkeynes@850 | 236 | [self emitMouseMoveEvent: event];
|
nkeynes@681 | 237 | }
|
nkeynes@681 | 238 |
|
nkeynes@681 | 239 | @end
|
nkeynes@681 | 240 |
|
nkeynes@681 | 241 | NSView *video_osx_create_drawable()
|
nkeynes@681 | 242 | {
|
nkeynes@681 | 243 | NSRect contentRect = {{0,0},{640,480}};
|
nkeynes@780 | 244 | video_view = [[LxdreamOSXView alloc] initWithFrame: contentRect];
|
nkeynes@681 | 245 | [video_view setAutoresizingMask: (NSViewWidthSizable|NSViewHeightSizable)];
|
nkeynes@681 | 246 | return video_view;
|
nkeynes@681 | 247 | }
|
nkeynes@681 | 248 |
|
nkeynes@681 | 249 | static gboolean video_osx_init()
|
nkeynes@681 | 250 | {
|
nkeynes@736 | 251 | if( video_view == NULL ) {
|
nkeynes@736 | 252 | return FALSE;
|
nkeynes@736 | 253 | }
|
nkeynes@736 | 254 | if( !video_nsgl_init_driver(video_view, &display_osx_driver) ) {
|
nkeynes@736 | 255 | return FALSE;
|
nkeynes@736 | 256 | }
|
nkeynes@736 | 257 | pvr2_setup_gl_context();
|
nkeynes@736 | 258 | return TRUE;
|
nkeynes@681 | 259 | }
|
nkeynes@681 | 260 |
|
nkeynes@681 | 261 | static void video_osx_shutdown()
|
nkeynes@681 | 262 | {
|
nkeynes@681 | 263 | }
|
nkeynes@681 | 264 |
|
nkeynes@681 | 265 | static void video_osx_display_blank( uint32_t colour )
|
nkeynes@681 | 266 | {
|
nkeynes@681 | 267 | }
|
nkeynes@681 | 268 |
|
nkeynes@681 | 269 | static int mac_keymap_cmp(const void *a, const void *b)
|
nkeynes@681 | 270 | {
|
nkeynes@681 | 271 | const gchar *key = a;
|
nkeynes@681 | 272 | const struct mac_keymap_struct *kb = b;
|
nkeynes@681 | 273 | return strcasecmp(key, kb->name);
|
nkeynes@681 | 274 | }
|
nkeynes@681 | 275 |
|
nkeynes@681 | 276 | static uint16_t video_osx_resolve_keysym( const gchar *keysym )
|
nkeynes@681 | 277 | {
|
nkeynes@681 | 278 | struct mac_keymap_struct *result = bsearch( keysym, mac_keysyms, mac_keysym_count, sizeof(struct mac_keymap_struct), mac_keymap_cmp );
|
nkeynes@681 | 279 | if( result == NULL ) {
|
nkeynes@681 | 280 | return 0;
|
nkeynes@681 | 281 | } else {
|
nkeynes@681 | 282 | return result->keycode + 1;
|
nkeynes@681 | 283 | }
|
nkeynes@681 | 284 | }
|
nkeynes@681 | 285 |
|
nkeynes@681 | 286 | static uint16_t video_osx_keycode_to_dckeysym(uint16_t keycode)
|
nkeynes@681 | 287 | {
|
nkeynes@681 | 288 | if( keycode < 1 || keycode > 128 ) {
|
nkeynes@681 | 289 | return DCKB_NONE;
|
nkeynes@681 | 290 | } else {
|
nkeynes@681 | 291 | return mac_keycode_to_dckeysym[keycode-1];
|
nkeynes@681 | 292 | }
|
nkeynes@681 | 293 | }
|
nkeynes@681 | 294 |
|
nkeynes@770 | 295 | static gchar *video_osx_keycode_to_keysym(uint16_t keycode)
|
nkeynes@770 | 296 | {
|
nkeynes@770 | 297 | if( keycode < 1 || keycode > 128 ) {
|
nkeynes@770 | 298 | return NULL;
|
nkeynes@770 | 299 | } else {
|
nkeynes@770 | 300 | return g_strdup(mac_keysyms_by_keycode[keycode-1]);
|
nkeynes@770 | 301 | }
|
nkeynes@770 | 302 | } |