Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Detect Media Key events on MacOSX
/*jshint moz: true, undef: true, unused: true */
/*global ctypes, require, console, exports */
let { Cu } = require('chrome');
let { setTimeout } = require('sdk/timers');
Cu.import('resource://gre/modules/ctypes.jsm');
var objc = ctypes.open(ctypes.libraryName('objc'));
var is64bit = ctypes.voidptr_t.size == 4 ? false : true;
// BASIC TYPES
var TYPES = {
char: ctypes.char,
id: ctypes.voidptr_t,
IMP: ctypes.voidptr_t,
SEL: ctypes.voidptr_t,
Class: ctypes.voidptr_t,
NSEventMask: ctypes.unsigned_long_long,
NSUInteger: is64bit ? ctypes.unsigned_long : ctypes.unsigned_int,
NSInteger: is64bit ? ctypes.long: ctypes.int,
BOOL: ctypes.signed_char,
NSEvent: ctypes.voidptr_t,
UInt16: ctypes.uint16_t
};
// advanced types
TYPES.NSEventType = TYPES.NSUInteger;
TYPES.NSEventMask = TYPES.NSUInteger;
// CONSTANTS
// event magic numbers from https://github.com/shannah/codenameone-avian/blob/84e2a17e99d2ff7db1da4246e833edb84e86f0f0/jdk7u-dev/build/macosx-x86_64/bridge_metadata/AppKit.headers/NSEvent.h#L79
var CONST = {
NSLeftMouseDown: 1, // TYPES.NSEventType
NSLeftMouseUp: 2, // TYPES.NSEventType
NSRightMouseDown: 3, // TYPES.NSEventType
NSRightMouseUp: 4, // TYPES.NSEventType
NSMouseMoved: 5, // TYPES.NSEventType
NSLeftMouseDragged: 6, // TYPES.NSEventType
NSRightMouseDragged: 7, // TYPES.NSEventType
NSMouseEntered: 8, // TYPES.NSEventType
NSMouseExited: 9, // TYPES.NSEventType
NSKeyDown: 10, // TYPES.NSEventType
NSKeyUp: 11, // TYPES.NSEventType
NSFlagsChanged: 12, // TYPES.NSEventType
NSAppKitDefined: 13, // TYPES.NSEventType
NSSystemDefined: 14, // TYPES.NSEventType
NSApplicationDefined: 15, // TYPES.NSEventType
NSPeriodic: 16, // TYPES.NSEventType
NSCursorUpdate: 17, // TYPES.NSEventType
NSScrollWheel: 22, // TYPES.NSEventType
NSTabletPoint: 23, // TYPES.NSEventType
NSTabletProximity: 24, // TYPES.NSEventType
NSOtherMouseDown: 25, // TYPES.NSEventType
NSOtherMouseUp: 26, // TYPES.NSEventType
NSOtherMouseDragged: 27, // TYPES.NSEventType
NSEventTypeGesture: 29, // TYPES.NSEventType
NSEventTypeMagnify: 30, // TYPES.NSEventType
NSEventTypeSwipe: 31, // TYPES.NSEventType
NSEventTypeRotate: 18, // TYPES.NSEventType
NSEventTypeBeginGesture: 19, // TYPES.NSEventType
NSEventTypeEndGesture: 20, // TYPES.NSEventType
NSEventTypeSmartMagnify: 32, // TYPES.NSEventType
NSEventTypeQuickLook: 33, // TYPES.NSEventType
NSEventTypePressure: 34, // TYPES.NSEventType
NSUIntegerMax: TYPES.NSUInteger(is64bit ? '0xffffffff' : '0xffff') // TYPES.NSUInteger
};
// the NSEventMask stuff is wrong in docs, the docs say here: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSEvent_Class/index.html#//apple_ref/doc/constant_group/NSEventMaskFromType
// the actual source code says here: /System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSEvent.h pasted the part here thanks to @arai - https://gist.github.com/Noitidart/9470ec02bd252e2ae7eb
// see chat with @arai and @capella on Aug 29 2015
CONST.NSLeftMouseDownMask = 1 << CONST.NSLeftMouseDown;
CONST.NSLeftMouseUpMask = 1 << CONST.NSLeftMouseUp;
CONST.NSRightMouseDownMask = 1 << CONST.NSRightMouseDown;
CONST.NSRightMouseUpMask = 1 << CONST.NSRightMouseUp;
CONST.NSMouseMovedMask = 1 << CONST.NSMouseMoved;
CONST.NSLeftMouseDraggedMask = 1 << CONST.NSLeftMouseDragged;
CONST.NSRightMouseDraggedMask = 1 << CONST.NSRightMouseDragged;
CONST.NSMouseEnteredMask = 1 << CONST.NSMouseEntered;
CONST.NSMouseExitedMask = 1 << CONST.NSMouseExited;
CONST.NSKeyDownMask = 1 << CONST.NSKeyDown;
CONST.NSKeyUpMask = 1 << CONST.NSKeyUp;
CONST.NSFlagsChangedMask = 1 << CONST.NSFlagsChanged;
CONST.NSAppKitDefinedMask = 1 << CONST.NSAppKitDefined;
CONST.NSSystemDefinedMask = 1 << CONST.NSSystemDefined;
CONST.NSApplicationDefinedMask = 1 << CONST.NSApplicationDefined;
CONST.NSPeriodicMask = 1 << CONST.NSPeriodic;
CONST.NSCursorUpdateMask = 1 << CONST.NSCursorUpdate;
CONST.NSScrollWheelMask = 1 << CONST.NSScrollWheel;
CONST.NSTabletPointMask = 1 << CONST.NSTabletPoint;
CONST.NSTabletProximityMask = 1 << CONST.NSTabletProximity;
CONST.NSOtherMouseDownMask = 1 << CONST.NSOtherMouseDown;
CONST.NSOtherMouseUpMask = 1 << CONST.NSOtherMouseUp;
CONST.NSOtherMouseDraggedMask = 1 << CONST.NSOtherMouseDragged;
CONST.NSEventMaskGesture = 1 << CONST.NSEventTypeGesture;
CONST.NSEventMaskMagnify = 1 << CONST.NSEventTypeMagnify;
CONST.NSEventMaskSwipe = 1 << CONST.NSEventTypeSwipe; // 1U << NSEventTypeSwipe
CONST.NSEventMaskRotate = 1 << CONST.NSEventTypeRotate;
CONST.NSEventMaskBeginGesture = 1 << CONST.NSEventTypeBeginGesture;
CONST.NSEventMaskEndGesture = 1 << CONST.NSEventTypeEndGesture;
CONST.NSEventMaskSmartMagnify = 1 << CONST.NSEventTypeSmartMagnify; // 1ULL << NSEventTypeSmartMagnify;
CONST.NSEventMaskPressure = 1 << CONST.NSEventTypePressure; // 1ULL << NSEventTypePressure
CONST.NSAnyEventMask = CONST.NSUIntegerMax; //0xffffffffU
// COMMON FUNCTIONS
var objc_getClass = objc.declare('objc_getClass', ctypes.default_abi, TYPES.id, TYPES.char.ptr);
var objc_msgSend = objc.declare('objc_msgSend', ctypes.default_abi, TYPES.id, TYPES.id, TYPES.SEL, '...');
var sel_registerName = objc.declare('sel_registerName', ctypes.default_abi, TYPES.SEL, TYPES.char.ptr);
// COMMON SELECTORS
var release = sel_registerName('release');
// OBJC HELPERS
function createBlock(aFuncTypePtr) {
/**
* Creates a C block instance from a JS Function.
* Blocks are regular Objective-C objects in Obj-C, and can be sent messages;
* thus Block instances need are creted using the core.wrapId() function.
*/
// Apple Docs :: Working with blocks - https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
var _NSConcreteGlobalBlock = objc.declare('_NSConcreteGlobalBlock', ctypes.voidptr_t); // https://dxr.mozilla.org/mozilla-central/source/js/src/ctypes/Library.cpp?offset=0#271
/**
* The "block descriptor" is a static singleton struct. Probably used in more
* complex Block scenarios involving actual closure variables needing storage
* (in `NodObjC`, JavaScript closures are leveraged instead).
*/
// struct is seen here in docs: http://clang.llvm.org/docs/Block-ABI-Apple.html
var Block_descriptor_1 = ctypes.StructType('Block_descriptor_1', [
{ reserved: ctypes.unsigned_long_long },
{ size: ctypes.unsigned_long_long }
]);
/**
* We have to simulate what the llvm compiler does when it encounters a Block
* literal expression (see `Block-ABI-Apple.txt` above).
* The "block literal" is the struct type for each Block instance.
*/
// struct is seen here in docs: http://clang.llvm.org/docs/Block-ABI-Apple.html
var Block_literal_1 = ctypes.StructType('Block_literal_1', [
{ isa: ctypes.voidptr_t },
{ flags: ctypes.int32_t },
{ reserved: ctypes.int32_t },
{ invoke: ctypes.voidptr_t },
{ descriptor: Block_descriptor_1.ptr }
]);
var BLOCK_CONST = {
BLOCK_HAS_COPY_DISPOSE: 1 << 25,
BLOCK_HAS_CTOR: 1 << 26,
BLOCK_IS_GLOBAL: 1 << 28,
BLOCK_HAS_STRET: 1 << 29,
BLOCK_HAS_SIGNATURE: 1 << 30
};
// based on work from here: https://github.com/trueinteractions/tint2/blob/f6ce18b16ada165b98b07869314dad1d7bee0252/modules/Bridge/core.js#L370-L394
var bl = Block_literal_1();
// Set the class of the instance
bl.isa = _NSConcreteGlobalBlock;
// Global flags
bl.flags = BLOCK_CONST.BLOCK_HAS_STRET;
bl.reserved = 0;
bl.invoke = aFuncTypePtr;
// create descriptor
var desc = Block_descriptor_1();
desc.reserved = 0;
desc.size = Block_literal_1.size;
// set descriptor into block literal
bl.descriptor = desc.address();
return bl;
}
// my personal globals for this code
var cancelFinally;
var releaseThese = [];
function shutdown() {
//put code here to unswizzle it
for (var i=0; i<releaseThese.length; i++) {
objc_msgSend(releaseThese[i], release);
}
objc.close();
};
var myHandler_js;
var myHandler_c;
var myBlock_c;
function main() {
var NSEvent = objc_getClass('NSEvent');
var addLocalMonitorForEventsMatchingMask = sel_registerName('addLocalMonitorForEventsMatchingMask:handler:');
var type = sel_registerName('subtype');
var data1 = sel_registerName('data1');
var myHandler_js = function(c_arg1__self, objc_arg1__aNSEventPtr) {
console.log('in myHandler', objc_arg1__aNSEventPtr);
try{
var cEventType = objc_msgSend(objc_arg1__aNSEventPtr, type);
console.info('cEventType:', cEventType);
cEventType = ctypes.cast(cEventType, TYPES.NSUInteger).value;
console.info('cEventType:', cEventType);
var eventData = objc_msgSend(objc_arg1__aNSEventPtr, data1);
console.info('eventData:', eventData);
eventData = ctypes.cast(eventData, TYPES.NSUInteger).value;
console.info('eventData:', eventData);
var cKeycode = eventData >>> 16;
console.info('cKeycode:', cKeycode);
console.info('key state:', ((eventData & 0xFF00) >> 8) == 0xA ? 'down' : 'up')
console.info('keyRepeat:', !!(eventData & 0x1))
} catch (exception){
console.error(exception);
}
return objc_arg1__aNSEventPtr; // return null to block
};
var IMP_for_EventMonitorCallback = ctypes.FunctionType(ctypes.default_abi, TYPES.NSEvent.ptr, [TYPES.id, TYPES.NSEvent.ptr]);
var myHandler_c = IMP_for_EventMonitorCallback.ptr(myHandler_js);
var myBlock_c = createBlock(myHandler_c);
console.info('myBlock_c:', myBlock_c, myBlock_c.toString());
console.info('myBlock_c.address():', myBlock_c.address(), myBlock_c.address().toString());
var rez_add = objc_msgSend(NSEvent, addLocalMonitorForEventsMatchingMask, TYPES.NSEventMask(CONST.NSSystemDefinedMask), myBlock_c.address());
console.log('rez_add:', rez_add, rez_add.toString());
cancelFinally = true;
setTimeout(function() {
console.log('time up');
var removeMonitor = sel_registerName('removeMonitor:');
var rez_remove = objc_msgSend(NSEvent, removeMonitor, rez_add);
console.log('rez_remove:', rez_remove, rez_remove.toString());
myHandler_js = null;
myHandler_c = null;
myBlock_c = null;
shutdown();
}, 50000);
}
try {
main();
} catch(ex) {
console.error('Exception Occoured:', ex);
} finally {
if (!cancelFinally) {
shutdown();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.