diff --git a/src/GtkApplicationDelegate.c b/src/GtkApplicationDelegate.c index e7ecd2c..9591533 100644 --- a/src/GtkApplicationDelegate.c +++ b/src/GtkApplicationDelegate.c @@ -24,6 +24,61 @@ #include #include "gtkosxapplication.h" + +CGEventRef eventTapFunction ( + CGEventTapProxy proxy, + CGEventType type, + CGEventRef event, + GtkApplicationDelegate *self +) { + fprintf(stderr, __FILE__": eventTapFunction()\n"); + if(type == kCGEventTapDisabledByTimeout || type == kCGEventTapDisabledByUserInput) + { + printf(__FILE__": reenable()\n"); + [self re_enable]; + } + if(type < 0 || type > 0x7fffffff){ + printf(__FILE__": eventTapFunction invalid type: %u\n", type); + return event; + } + NSEvent* nsevent = [NSEvent eventWithCGEvent:event]; + if( [nsevent type] == NSSystemDefined && [nsevent subtype] == 8 ) + { + int keyCode = (([nsevent data1] & 0xFFFF0000) >> 16); + int keyFlags = ([nsevent data1] & 0x0000FFFF); + gboolean keyDown = (((keyFlags & 0xFF00) >> 8)) == 0xA; + gboolean keyRepeat = (keyFlags & 0x1); + + printf(__FILE__ ": eventTapFunction mm(%i,%i,%i,%i)\n", + keyCode, + keyFlags, + keyDown, + keyRepeat); + + GtkosxApplication *app = g_object_new (GTKOSX_TYPE_APPLICATION, NULL); + guint sig = g_signal_lookup ("MultiMediaKey", + GTKOSX_TYPE_APPLICATION); + gboolean result = FALSE; + static gboolean inHandler = FALSE; + if (inHandler) return event; + if (sig) + { + inHandler = TRUE; + g_signal_emit (app, sig, 0, keyCode, keyDown, keyRepeat, &result); + } + else + { + printf(__FILE__ ": signal not found\n"); + } + + g_object_unref (app); + inHandler = FALSE; + if (result) + return NULL; + } + return event; +} + @implementation GtkApplicationDelegate -(BOOL) application: (NSApplication*)theApplication openFile: (NSString*) file { @@ -69,4 +124,49 @@ extern NSMenu* _gtkosx_application_dock_menu (GtkosxApplication* app); return _gtkosx_application_dock_menu (app); } +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification +{ + machPort = CGEventTapCreate(kCGSessionEventTap, + kCGHeadInsertEventTap, + kCGEventTapOptionDefault, + CGEventMaskBit(NSSystemDefined), + (CGEventTapCallBack)eventTapFunction, + self); + if (!machPort) { + NSException *e = + [NSException exceptionWithName:@"FailedToCreateKeyDownEventTap" + reason:@"Failed to create an event tap for key down events" + userInfo:[NSDictionary dictionary]]; + @throw e; + } + + machPortWrapper = + [[NSMachPort alloc] + initWithMachPort:CFMachPortGetPort(machPort) + options:NSMachPortDeallocateNone]; + + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + [runLoop addPort:machPortWrapper forMode:NSDefaultRunLoopMode]; +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification +{ + fprintf(stderr, __FILE__": applicationWillTerminate\n"); + if (machPortWrapper) { + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + [runLoop removePort:machPortWrapper forMode:NSDefaultRunLoopMode]; + [machPortWrapper release]; + machPortWrapper = NULL; + } + if (machPort) { + CFRelease(machPort); + machPort = NULL; + } +} + +- (void)re_enable +{ + CGEventTapEnable(machPort, true); +} + @end diff --git a/src/GtkApplicationDelegate.h b/src/GtkApplicationDelegate.h index 8fc01a6..78ab0b2 100644 --- a/src/GtkApplicationDelegate.h +++ b/src/GtkApplicationDelegate.h @@ -22,6 +22,11 @@ #import - interface GtkApplicationDelegate : NSObject {} + interface GtkApplicationDelegate : NSObject { + private + CFMachPortRef machPort; + NSMachPort *machPortWrapper; +} +-(void)re_enable; @end diff --git a/src/gtkosxapplication_quartz.c b/src/gtkosxapplication_quartz.c index d51298f..07bf6e3 100644 --- a/src/gtkosxapplication_quartz.c +++ b/src/gtkosxapplication_quartz.c @@ -444,6 +444,51 @@ g_cclosure_marshal_BOOLEAN__STRING (GClosure *closure, g_value_set_boolean (return_value, v_return); } +/* + * g_cclosure_marshal_BOOLEAN__INT_BOOLEAN_BOOLEAN: + * + * A private marshaller for handlers which take an int and to boolean parameters and + * return a boolean. + */ +static void +g_cclosure_marshal_BOOLEAN__INT_BOOLEAN_BOOLEAN (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__INT_BOOLEAN_BOOLEAN) (gpointer data1, + const int arg1, + const gboolean arg2, + const gboolean arg3, + gpointer data2); + register GMarshalFunc_BOOLEAN__INT_BOOLEAN_BOOLEAN callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (n_param_values == 4); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__INT_BOOLEAN_BOOLEAN) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_value_get_int (param_values + 1), + g_value_get_boolean (param_values + 2), + g_value_get_boolean (param_values + 3), + data2); + g_value_set_boolean (return_value, v_return); +} /* * block_termination_accumulator: @@ -491,6 +536,22 @@ global_event_filter_func (gpointer windowing_event, GdkEvent *event, NSEvent *nsevent = windowing_event; GtkosxApplication* app = user_data; + //ELL + if( [nsevent type] == NSSystemDefined && [nsevent subtype] == 8 ) + { + int keyCode = (([nsevent data1] & 0xFFFF0000) >> 16); + int keyFlags = ([nsevent data1] & 0x0000FFFF); + int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA; + int keyRepeat = (keyFlags & 0x1); + + printf(__FILE__ ": global_event_filter_func mm(%i,%i,%i,%i)\n", + keyCode, + keyFlags, + keyState, + keyRepeat); + } + + /* Handle menu events with no window, since they won't go through * the regular event processing. When using Gtk before 3.6, we have * to release the gdk mutex so that we can recquire it when we @@ -526,6 +587,7 @@ enum BlockTermination, WillTerminate, OpenFile, + MultiMediaKey, LastSignal }; @@ -667,6 +729,24 @@ gtkosx_application_class_init (GtkosxApplicationClass *klass) g_cclosure_marshal_BOOLEAN__STRING, G_TYPE_BOOLEAN, 1, G_TYPE_STRING); + /** + * GtkosxApplication::MultiMediaKey: + * @app: The application object + * @keyCode: code of the key + * @isDown: is the key pressed + * @repeat: key repeat? + * @user_data: Data attached at connection + * + * Emitted when a multimedia key is pressed or released. + * + */ + gtkosx_application_signals[MultiMediaKey] = + g_signal_new ("MultiMediaKey", + GTKOSX_TYPE_APPLICATION, + G_SIGNAL_NO_RECURSE | G_SIGNAL_ACTION, + 0, NULL, NULL, + g_cclosure_marshal_BOOLEAN__INT_BOOLEAN_BOOLEAN, + G_TYPE_BOOLEAN, 3, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); } /**