mirror of https://github.com/sipwise/jitsi.git
parent
2b1ed218df
commit
6ea38de334
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,86 @@
|
||||
/*
|
||||
DDHotKey -- DDHotKeyCenter.h
|
||||
|
||||
Copyright (c) 2010, Dave DeLong <http://www.davedelong.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
The software is provided "as is", without warranty of any kind, including all implied warranties of merchantability and fitness. In no event shall the author(s) or copyright holder(s) be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#if NS_BLOCKS_AVAILABLE
|
||||
//a convenient typedef for the required signature of a hotkey block callback
|
||||
typedef void (^DDHotKeyTask)(NSEvent*);
|
||||
#endif
|
||||
|
||||
@interface DDHotKey : NSObject
|
||||
|
||||
@property (nonatomic, readonly, retain) id target;
|
||||
@property (nonatomic, readonly) SEL action;
|
||||
@property (nonatomic, readonly, retain) id object;
|
||||
#if NS_BLOCKS_AVAILABLE
|
||||
@property (nonatomic, readonly, copy) DDHotKeyTask task;
|
||||
#endif
|
||||
|
||||
@property (nonatomic, readonly) unsigned short keyCode;
|
||||
@property (nonatomic, readonly) NSUInteger modifierFlags;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface DDHotKeyCenter : NSObject {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Register a target/action hotkey.
|
||||
The modifierFlags must be a bitwise OR of NSCommandKeyMask, NSAlternateKeyMask, NSControlKeyMask, or NSShiftKeyMask;
|
||||
Returns YES if the hotkey was registered; NO otherwise.
|
||||
*/
|
||||
- (BOOL) registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags target:(id)target action:(SEL)action object:(id)object;
|
||||
|
||||
#if NS_BLOCKS_AVAILABLE
|
||||
/**
|
||||
Register a block callback hotkey.
|
||||
The modifierFlags must be a bitwise OR of NSCommandKeyMask, NSAlternateKeyMask, NSControlKeyMask, or NSShiftKeyMask;
|
||||
Returns YES if the hotkey was registered; NO otherwise.
|
||||
*/
|
||||
- (BOOL) registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task;
|
||||
#endif
|
||||
|
||||
/**
|
||||
See if a hotkey exists with the specified keycode and modifier flags.
|
||||
NOTE: this will only check among hotkeys you have explicitly registered with DDHotKeyCenter. This does not check all globally registered hotkeys.
|
||||
*/
|
||||
- (BOOL) hasRegisteredHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags;
|
||||
|
||||
/**
|
||||
Unregister a specific hotkey
|
||||
*/
|
||||
- (void) unregisterHotKey:(DDHotKey *)hotKey;
|
||||
|
||||
/**
|
||||
Unregister all hotkeys with a specific target
|
||||
*/
|
||||
- (void) unregisterHotKeysWithTarget:(id)target;
|
||||
|
||||
/**
|
||||
Unregister all hotkeys with a specific target and action
|
||||
*/
|
||||
- (void) unregisterHotKeysWithTarget:(id)target action:(SEL)action;
|
||||
|
||||
/**
|
||||
Unregister a hotkey with a specific keycode and modifier flags
|
||||
*/
|
||||
- (void) unregisterHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags;
|
||||
|
||||
/**
|
||||
Returns a set of currently registered hotkeys
|
||||
**/
|
||||
- (NSSet *) registeredHotKeys;
|
||||
|
||||
@end
|
||||
|
||||
@ -0,0 +1,313 @@
|
||||
/*
|
||||
DDHotKey -- DDHotKeyCenter.m
|
||||
|
||||
Copyright (c) 2010, Dave DeLong <http://www.davedelong.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
The software is provided "as is", without warranty of any kind, including all implied warranties of merchantability and fitness. In no event shall the author(s) or copyright holder(s) be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
|
||||
*/
|
||||
|
||||
#import "DDHotKeyCenter.h"
|
||||
#import <Carbon/Carbon.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#pragma mark Private Global Declarations
|
||||
|
||||
static NSMutableSet * _registeredHotKeys = nil;
|
||||
static UInt32 _nextHotKeyID = 1;
|
||||
OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void * userData);
|
||||
UInt32 dd_translateModifierFlags(NSUInteger flags);
|
||||
NSString* dd_stringifyModifierFlags(NSUInteger flags);
|
||||
|
||||
#pragma mark DDHotKey
|
||||
|
||||
@implementation DDHotKey
|
||||
|
||||
- (id) target { return nil; }
|
||||
- (SEL) action { return nil; }
|
||||
- (id) object { return nil; }
|
||||
- (unsigned short) keyCode { return 0; }
|
||||
- (NSUInteger) modifierFlags { return 0; }
|
||||
|
||||
#if NS_BLOCKS_AVAILABLE
|
||||
- (DDHotKeyTask) task { return nil; }
|
||||
#endif
|
||||
|
||||
- (NSUInteger) hash {
|
||||
return [self keyCode] + [self modifierFlags];
|
||||
}
|
||||
|
||||
- (BOOL) isEqual:(id)object {
|
||||
BOOL equal = NO;
|
||||
if ([object isKindOfClass:[DDHotKey class]]) {
|
||||
equal = ([object keyCode] == [self keyCode]);
|
||||
equal &= ([object modifierFlags] == [self modifierFlags]);
|
||||
}
|
||||
return equal;
|
||||
}
|
||||
|
||||
- (NSString *) description {
|
||||
NSString * flags = dd_stringifyModifierFlags([self modifierFlags]);
|
||||
NSString * invokes = @"(block)";
|
||||
if ([self target] != nil && [self action] != nil) {
|
||||
invokes = [NSString stringWithFormat:@"[%@ %@]", [self target], NSStringFromSelector([self action])];
|
||||
}
|
||||
return [NSString stringWithFormat:@"%@\n\t(key: %hu\n\tflags: %@\n\tinvokes: %@)", [super description], [self keyCode], flags, invokes];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface _DDHotKey : DDHotKey {
|
||||
@private
|
||||
id target;
|
||||
SEL action;
|
||||
id object;
|
||||
|
||||
#if NS_BLOCKS_AVAILABLE
|
||||
DDHotKeyTask task;
|
||||
#endif
|
||||
|
||||
unsigned short keyCode;
|
||||
NSUInteger modifierFlags;
|
||||
UInt32 hotKeyID;
|
||||
NSValue * hotKeyRef;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) id target;
|
||||
@property (nonatomic) SEL action;
|
||||
@property (nonatomic, retain) id object;
|
||||
@property (nonatomic) unsigned short keyCode;
|
||||
@property (nonatomic) NSUInteger modifierFlags;
|
||||
@property (nonatomic) UInt32 hotKeyID;
|
||||
@property (nonatomic, retain) NSValue * hotKeyRef;
|
||||
|
||||
#if NS_BLOCKS_AVAILABLE
|
||||
@property (nonatomic, copy) DDHotKeyTask task;
|
||||
#endif
|
||||
|
||||
- (void) invokeWithEvent:(NSEvent *)event;
|
||||
- (BOOL) registerHotKey;
|
||||
- (void) unregisterHotKey;
|
||||
|
||||
@end
|
||||
|
||||
@implementation _DDHotKey
|
||||
|
||||
@synthesize target, action, object, keyCode, modifierFlags, hotKeyID, hotKeyRef;
|
||||
#if NS_BLOCKS_AVAILABLE
|
||||
@synthesize task;
|
||||
#endif
|
||||
|
||||
- (Class) class { return [DDHotKey class]; }
|
||||
|
||||
- (void) invokeWithEvent:(NSEvent *)event {
|
||||
if (target != nil && action != nil && [target respondsToSelector:action]) {
|
||||
[target performSelector:action withObject:event withObject:object];
|
||||
}
|
||||
#if NS_BLOCKS_AVAILABLE
|
||||
else if (task != nil) {
|
||||
task(event);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSString *) actionString {
|
||||
return NSStringFromSelector(action);
|
||||
}
|
||||
|
||||
- (BOOL) registerHotKey {
|
||||
EventHotKeyID keyID;
|
||||
keyID.signature = 'htk1';
|
||||
keyID.id = _nextHotKeyID;
|
||||
|
||||
EventHotKeyRef carbonHotKey;
|
||||
UInt32 flags = dd_translateModifierFlags(modifierFlags);
|
||||
OSStatus err = RegisterEventHotKey(keyCode, flags, keyID, GetEventDispatcherTarget(), 0, &carbonHotKey);
|
||||
|
||||
//error registering hot key
|
||||
if (err != 0) { return NO; }
|
||||
|
||||
NSValue * refValue = [NSValue valueWithPointer:carbonHotKey];
|
||||
[self setHotKeyRef:refValue];
|
||||
[self setHotKeyID:_nextHotKeyID];
|
||||
|
||||
_nextHotKeyID++;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) unregisterHotKey {
|
||||
EventHotKeyRef carbonHotKey = (EventHotKeyRef)[hotKeyRef pointerValue];
|
||||
UnregisterEventHotKey(carbonHotKey);
|
||||
[self setHotKeyRef:nil];
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
[target release], target = nil;
|
||||
[object release], object = nil;
|
||||
if (hotKeyRef != nil) {
|
||||
[self unregisterHotKey];
|
||||
[hotKeyRef release], hotKeyRef = nil;
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark DDHotKeyCenter
|
||||
|
||||
@implementation DDHotKeyCenter
|
||||
|
||||
+ (void) initialize {
|
||||
if (self == [DDHotKeyCenter class] && _registeredHotKeys == nil) {
|
||||
_registeredHotKeys = [[NSMutableSet alloc] init];
|
||||
_nextHotKeyID = 1;
|
||||
EventTypeSpec eventSpec;
|
||||
eventSpec.eventClass = kEventClassKeyboard;
|
||||
eventSpec.eventKind = kEventHotKeyReleased;
|
||||
InstallApplicationEventHandler(&dd_hotKeyHandler, 1, &eventSpec, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSSet *) hotKeysMatchingPredicate:(NSPredicate *)predicate {
|
||||
return [_registeredHotKeys filteredSetUsingPredicate:predicate];
|
||||
}
|
||||
|
||||
- (BOOL) hasRegisteredHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags {
|
||||
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, flags];
|
||||
return ([[self hotKeysMatchingPredicate:predicate] count] > 0);
|
||||
}
|
||||
|
||||
#if NS_BLOCKS_AVAILABLE
|
||||
- (BOOL) registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task {
|
||||
//we can't add a new hotkey if something already has this combo
|
||||
if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; }
|
||||
|
||||
_DDHotKey * newHotKey = [[_DDHotKey alloc] init];
|
||||
[newHotKey setTask:task];
|
||||
[newHotKey setKeyCode:keyCode];
|
||||
[newHotKey setModifierFlags:flags];
|
||||
|
||||
BOOL success = [newHotKey registerHotKey];
|
||||
if (success) {
|
||||
[_registeredHotKeys addObject:newHotKey];
|
||||
}
|
||||
|
||||
[newHotKey release];
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (BOOL) registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags target:(id)target action:(SEL)action object:(id)object {
|
||||
//we can't add a new hotkey if something already has this combo
|
||||
if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; }
|
||||
|
||||
//build the hotkey object:
|
||||
_DDHotKey * newHotKey = [[_DDHotKey alloc] init];
|
||||
[newHotKey setTarget:target];
|
||||
[newHotKey setAction:action];
|
||||
[newHotKey setObject:object];
|
||||
[newHotKey setKeyCode:keyCode];
|
||||
[newHotKey setModifierFlags:flags];
|
||||
|
||||
BOOL success = [newHotKey registerHotKey];
|
||||
if (success) {
|
||||
[_registeredHotKeys addObject:newHotKey];
|
||||
}
|
||||
|
||||
[newHotKey release];
|
||||
return success;
|
||||
}
|
||||
|
||||
- (void) unregisterHotKeysMatchingPredicate:(NSPredicate *)predicate {
|
||||
//explicitly unregister the hotkey, since relying on the unregistration in -dealloc can be problematic
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSSet * matches = [self hotKeysMatchingPredicate:predicate];
|
||||
[_registeredHotKeys minusSet:matches];
|
||||
[matches makeObjectsPerformSelector:@selector(unregisterHotKey)];
|
||||
[pool release];
|
||||
}
|
||||
|
||||
- (void) unregisterHotKey:(DDHotKey *)hotKey {
|
||||
if (object_getClass(hotKey) == [_DDHotKey class]) {
|
||||
_DDHotKey * key = (_DDHotKey *)hotKey;
|
||||
[_registeredHotKeys removeObject:key];
|
||||
[key unregisterHotKey];
|
||||
} else {
|
||||
[NSException raise:NSInvalidArgumentException format:@"Invalid hotkey"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) unregisterHotKeysWithTarget:(id)target {
|
||||
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"target = %@", target];
|
||||
[self unregisterHotKeysMatchingPredicate:predicate];
|
||||
}
|
||||
|
||||
- (void) unregisterHotKeysWithTarget:(id)target action:(SEL)action {
|
||||
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"target = %@ AND actionString = %@", target, NSStringFromSelector(action)];
|
||||
[self unregisterHotKeysMatchingPredicate:predicate];
|
||||
}
|
||||
|
||||
- (void) unregisterHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags {
|
||||
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, flags];
|
||||
[self unregisterHotKeysMatchingPredicate:predicate];
|
||||
}
|
||||
|
||||
- (NSSet *) registeredHotKeys {
|
||||
return [self hotKeysMatchingPredicate:[NSPredicate predicateWithFormat:@"hotKeyRef != NULL"]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void * userData) {
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
EventHotKeyID hotKeyID;
|
||||
GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotKeyID),NULL,&hotKeyID);
|
||||
|
||||
UInt32 keyID = hotKeyID.id;
|
||||
|
||||
NSSet * matchingHotKeys = [_registeredHotKeys filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"hotKeyID = %u", keyID]];
|
||||
if ([matchingHotKeys count] > 1) { NSLog(@"ERROR!"); }
|
||||
_DDHotKey * matchingHotKey = [matchingHotKeys anyObject];
|
||||
|
||||
NSEvent * event = [NSEvent eventWithEventRef:theEvent];
|
||||
NSEvent * keyEvent = [NSEvent keyEventWithType:NSKeyUp
|
||||
location:[event locationInWindow]
|
||||
modifierFlags:[event modifierFlags]
|
||||
timestamp:[event timestamp]
|
||||
windowNumber:-1
|
||||
context:nil
|
||||
characters:@""
|
||||
charactersIgnoringModifiers:@""
|
||||
isARepeat:NO
|
||||
keyCode:[matchingHotKey keyCode]];
|
||||
|
||||
[matchingHotKey invokeWithEvent:keyEvent];
|
||||
|
||||
[pool release];
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
UInt32 dd_translateModifierFlags(NSUInteger flags) {
|
||||
UInt32 newFlags = 0;
|
||||
if ((flags & NSControlKeyMask) > 0) { newFlags |= controlKey; }
|
||||
if ((flags & NSCommandKeyMask) > 0) { newFlags |= cmdKey; }
|
||||
if ((flags & NSShiftKeyMask) > 0) { newFlags |= shiftKey; }
|
||||
if ((flags & NSAlternateKeyMask) > 0) { newFlags |= optionKey; }
|
||||
return newFlags;
|
||||
}
|
||||
|
||||
NSString* dd_stringifyModifierFlags(NSUInteger flags) {
|
||||
NSMutableArray * bits = [NSMutableArray array];
|
||||
if ((flags & NSControlKeyMask) > 0) { [bits addObject:@"NSControlKeyMask"]; }
|
||||
if ((flags & NSCommandKeyMask) > 0) { [bits addObject:@"NSCommandKeyMask"]; }
|
||||
if ((flags & NSShiftKeyMask) > 0) { [bits addObject:@"NSShiftKeyMask"]; }
|
||||
if ((flags & NSAlternateKeyMask) > 0) { [bits addObject:@"NSAlternateKeyMask"]; }
|
||||
if ([bits count] > 0) {
|
||||
return [NSString stringWithFormat:@"(%@)", [bits componentsJoinedByString:@" | "]];
|
||||
}
|
||||
return @"ERROR: No valid flags";
|
||||
}
|
||||
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Jitsi Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
|
||||
#ifndef JAVAKEY_H
|
||||
#define JAVAKEY_H
|
||||
|
||||
/**
|
||||
* \enum java_keycode
|
||||
* \brief Java keycode
|
||||
*/
|
||||
enum java_keycode
|
||||
{
|
||||
JVK_PAUSE = 0x13,
|
||||
JVK_CAPS_LOCK = 0x14,
|
||||
JVK_ESCAPE = 0x1B,
|
||||
JVK_SPACE =0x20,
|
||||
JVK_UP = 0x26,
|
||||
JVK_DOWN = 0x28,
|
||||
JVK_LEFT = 0x25,
|
||||
JVK_RIGHT = 0x27,
|
||||
JVK_COMMA = 0x2C,
|
||||
JVK_MINUS = 0x2D,
|
||||
JVK_PERIOD = 0x2E,
|
||||
JVK_SLASH = 0x2F,
|
||||
JVK_0 = 0x30,
|
||||
JVK_1 = 0x31,
|
||||
JVK_2 = 0x32,
|
||||
JVK_3 = 0x33,
|
||||
JVK_4 = 0x34,
|
||||
JVK_5 = 0x35,
|
||||
JVK_6 = 0x36,
|
||||
JVK_7 = 0x37,
|
||||
JVK_8 = 0x38,
|
||||
JVK_9 = 0x39,
|
||||
JVK_SEMICOLON = 0x3B,
|
||||
JVK_EQUALS = 0x3D,
|
||||
JVK_A = 0x41,
|
||||
JVK_B = 0x42,
|
||||
JVK_C = 0x43,
|
||||
JVK_D = 0x44,
|
||||
JVK_E = 0x45,
|
||||
JVK_F = 0x46,
|
||||
JVK_G = 0x47,
|
||||
JVK_H = 0x48,
|
||||
JVK_I = 0x49,
|
||||
JVK_J = 0x4A,
|
||||
JVK_K = 0x4B,
|
||||
JVK_L = 0x4C,
|
||||
JVK_M = 0x4D,
|
||||
JVK_N = 0x4E,
|
||||
JVK_O = 0x4F,
|
||||
JVK_P = 0x50,
|
||||
JVK_Q = 0x51,
|
||||
JVK_R = 0x52,
|
||||
JVK_S = 0x53,
|
||||
JVK_T = 0x54,
|
||||
JVK_U = 0x55,
|
||||
JVK_V = 0x56,
|
||||
JVK_W = 0x57,
|
||||
JVK_X = 0x58,
|
||||
JVK_Y = 0x59,
|
||||
JVK_Z = 0x5A,
|
||||
JVK_OPEN_BRACKET = 0x5B,
|
||||
JVK_BACK_SLASH = 0x5C,
|
||||
JVK_CLOSE_BRACKET = 0x5D,
|
||||
JVK_NUMPAD0 = 0x60,
|
||||
JVK_NUMPAD1 = 0x61,
|
||||
JVK_NUMPAD2 = 0x62,
|
||||
JVK_NUMPAD3 = 0x63,
|
||||
JVK_NUMPAD4 = 0x64,
|
||||
JVK_NUMPAD5 = 0x65,
|
||||
JVK_NUMPAD6 = 0x66,
|
||||
JVK_NUMPAD7 = 0x67,
|
||||
JVK_NUMPAD8 = 0x68,
|
||||
JVK_NUMPAD9 = 0x69,
|
||||
JVK_KP_UP = 0xE0,
|
||||
JVK_KP_DOWN = 0xE1,
|
||||
JVK_KP_LEFT = 0xE2,
|
||||
JVK_KP_RIGHT = 0xE3,
|
||||
JVK_MULTIPLY = 0x6A,
|
||||
JVK_ADD = 0x6B,
|
||||
JVK_SEPARATOR = 0x6C,
|
||||
JVK_SUBTRACT = 0x6D,
|
||||
JVK_DECIMAL = 0x6E,
|
||||
JVK_DIVIDE = 0x6F,
|
||||
JVK_DELETE = 0x7F,
|
||||
JVK_NUM_LOCK = 0x90,
|
||||
JVK_CLEAR = 0x03,
|
||||
JVK_SCROLL_LOCK = 0x91,
|
||||
JVK_F1 = 0x70,
|
||||
JVK_F2 = 0x71,
|
||||
JVK_F3 = 0x72,
|
||||
JVK_F4 = 0x73,
|
||||
JVK_F5 = 0x74,
|
||||
JVK_F6= 0x75,
|
||||
JVK_F7 = 0x76,
|
||||
JVK_F8 = 0x77,
|
||||
JVK_F9 = 0x78,
|
||||
JVK_F10 = 0x79,
|
||||
JVK_F11 = 0x7A,
|
||||
JVK_F12 = 0x7B,
|
||||
JVK_F13 = 0xF000,
|
||||
JVK_F14 = 0xF001,
|
||||
JVK_F15 = 0xF002,
|
||||
JVK_F16 = 0xF003,
|
||||
JVK_F17 = 0xF004,
|
||||
JVK_F18 = 0xF005,
|
||||
JVK_F19 = 0xF006,
|
||||
JVK_F20 = 0xF007,
|
||||
JVK_F21 = 0xF008,
|
||||
JVK_F22 = 0xF009,
|
||||
JVK_F23 = 0xF00A,
|
||||
JVK_F24 = 0xF00B,
|
||||
JVK_PRINTSCREEN = 0x9A,
|
||||
JVK_INSERT = 0x9B,
|
||||
JVK_HELP = 0x9C,
|
||||
JVK_PAGE_UP = 0x21,
|
||||
JVK_PAGE_DOWN = 0x22,
|
||||
JVK_HOME = 0x24,
|
||||
JVK_END = 0x23,
|
||||
JVK_BACK_QUOTE = 0xC0,
|
||||
JVK_QUOTE = 0xDE,
|
||||
JVK_DEAD_GRAVE = 0x80,
|
||||
JVK_DEAD_ACUTE = 0x81,
|
||||
JVK_DEAD_CIRCUMFLEX = 0x82,
|
||||
JVK_DEAD_TILDE = 0x83,
|
||||
JVK_DEAD_MACRON = 0x84,
|
||||
JVK_DEAD_BREVE = 0x85,
|
||||
JVK_DEAD_ABOVEDOT = 0x86,
|
||||
JVK_DEAD_DIAERESIS = 0x87,
|
||||
JVK_DEAD_ABOVERING = 0x88,
|
||||
JVK_DEAD_DOUBLEACUTE = 0x89,
|
||||
JVK_DEAD_CARON = 0x8A,
|
||||
JVK_DEAD_CEDILLA = 0x8B,
|
||||
JVK_DEAD_OGONEK = 0x8C,
|
||||
JVK_DEAD_IOTA = 0x8D,
|
||||
JVK_AMPERSAND = 0x96,
|
||||
JVK_ASTERISK = 0x97,
|
||||
JVK_QUOTEDBL = 0x98,
|
||||
JVK_LESS = 0x99,
|
||||
JVK_GREATER = 0xA0,
|
||||
JVK_BRACELEFT = 0xA1,
|
||||
JVK_BRACERIGHT = 0xA2,
|
||||
JVK_AT = 0x0200,
|
||||
JVK_COLON = 0x0201,
|
||||
JVK_CIRCUMFLEX = 0x0202,
|
||||
JVK_DOLLAR = 0x0203,
|
||||
JVK_EURO_SIGN = 0x0204,
|
||||
JVK_EXCLAMATION_MARK = 0x0205,
|
||||
JVK_INVERTED_EXCLAMATION_MARK = 0x0206,
|
||||
JVK_LEFT_PARENTHESIS = 0x0207,
|
||||
JVK_NUMBER_SIGN = 0x0208,
|
||||
JVK_PLUS = 0x0209,
|
||||
JVK_RIGHT_PARENTHESIS = 0x020A,
|
||||
JVK_UNDERSCORE = 0x020B,
|
||||
};
|
||||
|
||||
#endif /* JAVAKEY_H */
|
||||
|
||||
@ -0,0 +1,924 @@
|
||||
/*
|
||||
* Jitsi Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
|
||||
#include "net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook.h"
|
||||
|
||||
/* Linux specific code */
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/keysymdef.h>
|
||||
|
||||
#include "javakey.h"
|
||||
|
||||
/**
|
||||
* \class keystrok
|
||||
* \brief keystrok class.
|
||||
*/
|
||||
class keystrok
|
||||
{
|
||||
public:
|
||||
int vkcode; /**< Virtual keycode. */
|
||||
int modifiers; /**< Modifiers (ALT, CTLR, ...). */
|
||||
int active; /**< If the hotkey is active. */
|
||||
};
|
||||
|
||||
/**
|
||||
* \class keyboard_hook
|
||||
* \brief keyboard_hook structure.
|
||||
*/
|
||||
class keyboard_hook
|
||||
{
|
||||
public:
|
||||
Display* display; /**< X11 display. */
|
||||
Window root; /**< X11 root window. */
|
||||
jobject delegate; /**< Java object delegate. */
|
||||
JavaVM* jvm; /**< Java VM. */
|
||||
volatile int running; /**< Running state. */
|
||||
std::list<keystrok> keystrokes; /**< List of keystrokes registered. */
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Convert Java keycode to native ones.
|
||||
* \param keycode Java keycode
|
||||
* \return native X11 keycode
|
||||
*/
|
||||
static int convertJavaKeycodeToX11(int keycode)
|
||||
{
|
||||
int ret = -1;
|
||||
switch(keycode)
|
||||
{
|
||||
/* 0 - 9 */
|
||||
case JVK_0:
|
||||
ret = XK_0;
|
||||
break;
|
||||
case JVK_1:
|
||||
ret = XK_1;
|
||||
break;
|
||||
case JVK_2:
|
||||
ret = XK_2;
|
||||
break;
|
||||
case JVK_3:
|
||||
ret = XK_3;
|
||||
break;
|
||||
case JVK_4:
|
||||
ret = XK_4;
|
||||
break;
|
||||
case JVK_5:
|
||||
ret = XK_5;
|
||||
break;
|
||||
case JVK_6:
|
||||
ret = XK_6;
|
||||
break;
|
||||
case JVK_7:
|
||||
ret = XK_7;
|
||||
break;
|
||||
case JVK_8:
|
||||
ret = XK_8;
|
||||
break;
|
||||
case JVK_9:
|
||||
ret = XK_9;
|
||||
break;
|
||||
/* A - Z */
|
||||
case JVK_A:
|
||||
ret = XK_A;
|
||||
break;
|
||||
case JVK_B:
|
||||
ret = XK_B;
|
||||
break;
|
||||
case JVK_C:
|
||||
ret = XK_C;
|
||||
break;
|
||||
case JVK_D:
|
||||
ret = XK_D;
|
||||
break;
|
||||
case JVK_E:
|
||||
ret = XK_E;
|
||||
break;
|
||||
case JVK_F:
|
||||
ret = XK_F;
|
||||
break;
|
||||
case JVK_G:
|
||||
ret = XK_G;
|
||||
break;
|
||||
case JVK_H:
|
||||
ret = XK_H;
|
||||
break;
|
||||
case JVK_I:
|
||||
ret = XK_I;
|
||||
break;
|
||||
case JVK_J:
|
||||
ret = XK_J;
|
||||
break;
|
||||
case JVK_K:
|
||||
ret = XK_K;
|
||||
break;
|
||||
case JVK_L:
|
||||
ret = XK_L;
|
||||
break;
|
||||
case JVK_M:
|
||||
ret = XK_M;
|
||||
break;
|
||||
case JVK_N:
|
||||
ret = XK_N;
|
||||
break;
|
||||
case JVK_O:
|
||||
ret = XK_O;
|
||||
break;
|
||||
case JVK_P:
|
||||
ret = XK_P;
|
||||
break;
|
||||
case JVK_Q:
|
||||
ret = XK_Q;
|
||||
break;
|
||||
case JVK_R:
|
||||
ret = XK_R;
|
||||
break;
|
||||
case JVK_S:
|
||||
ret = XK_S;
|
||||
break;
|
||||
case JVK_T:
|
||||
ret = XK_T;
|
||||
break;
|
||||
case JVK_U:
|
||||
ret = XK_U;
|
||||
break;
|
||||
case JVK_V:
|
||||
ret = XK_V;
|
||||
break;
|
||||
case JVK_W:
|
||||
ret = XK_W;
|
||||
break;
|
||||
case JVK_X:
|
||||
ret = XK_X;
|
||||
break;
|
||||
case JVK_Y:
|
||||
ret = XK_Y;
|
||||
break;
|
||||
case JVK_Z:
|
||||
ret = XK_Z;
|
||||
break;
|
||||
/* F1 - F12 */
|
||||
case JVK_F1:
|
||||
ret = XK_F1;
|
||||
break;
|
||||
case JVK_F2:
|
||||
ret = XK_F2;
|
||||
break;
|
||||
case JVK_F3:
|
||||
ret = XK_F3;
|
||||
break;
|
||||
case JVK_F4:
|
||||
ret = XK_F4;
|
||||
break;
|
||||
case JVK_F5:
|
||||
ret = XK_F5;
|
||||
break;
|
||||
case JVK_F6:
|
||||
ret = XK_F6;
|
||||
break;
|
||||
case JVK_F7:
|
||||
ret = XK_F7;
|
||||
break;
|
||||
case JVK_F8:
|
||||
ret = XK_F8;
|
||||
break;
|
||||
case JVK_F9:
|
||||
ret = XK_F9;
|
||||
break;
|
||||
case JVK_F10:
|
||||
ret = XK_F10;
|
||||
break;
|
||||
case JVK_F11:
|
||||
ret = XK_F11;
|
||||
break;
|
||||
case JVK_F12:
|
||||
ret = XK_F12;
|
||||
break;
|
||||
/* arrows (left, right, up, down) */
|
||||
case JVK_LEFT:
|
||||
ret = XK_Left;
|
||||
break;
|
||||
case JVK_RIGHT:
|
||||
ret = XK_Right;
|
||||
break;
|
||||
case JVK_UP:
|
||||
ret = XK_Up;
|
||||
break;
|
||||
case JVK_DOWN:
|
||||
ret = XK_Down;
|
||||
break;
|
||||
case JVK_COMMA:
|
||||
ret = XK_comma;
|
||||
break;
|
||||
case JVK_MINUS:
|
||||
ret = XK_minus;
|
||||
break;
|
||||
case JVK_PLUS:
|
||||
ret = XK_plus;
|
||||
break;
|
||||
case JVK_PERIOD:
|
||||
ret = XK_period;
|
||||
break;
|
||||
case JVK_SLASH:
|
||||
ret = XK_slash;
|
||||
break;
|
||||
case JVK_BACK_SLASH:
|
||||
ret = XK_backslash;
|
||||
break;
|
||||
case JVK_SEMICOLON:
|
||||
ret = XK_semicolon;
|
||||
break;
|
||||
case JVK_EQUALS:
|
||||
ret = XK_equal;
|
||||
break;
|
||||
case JVK_COLON:
|
||||
ret = XK_colon;
|
||||
break;
|
||||
case JVK_UNDERSCORE:
|
||||
ret = XK_underscore;
|
||||
break;
|
||||
case JVK_DOLLAR:
|
||||
ret = XK_dollar;
|
||||
break;
|
||||
case JVK_EXCLAMATION_MARK:
|
||||
ret = XK_exclam;
|
||||
break;
|
||||
case JVK_GREATER:
|
||||
ret = XK_greater;
|
||||
break;
|
||||
case JVK_LESS:
|
||||
ret = XK_less;
|
||||
break;
|
||||
case JVK_QUOTE:
|
||||
ret = XK_quoteleft;
|
||||
break;
|
||||
case JVK_BACK_QUOTE:
|
||||
ret = XK_quoteright;
|
||||
break;
|
||||
case JVK_INSERT:
|
||||
ret = XK_Insert;
|
||||
break;
|
||||
case JVK_HELP:
|
||||
ret = XK_Help;
|
||||
break;
|
||||
case JVK_HOME:
|
||||
ret = XK_Home;
|
||||
break;
|
||||
case JVK_END:
|
||||
ret = XK_End;
|
||||
break;
|
||||
case JVK_PAGE_UP:
|
||||
ret = XK_Page_Up;
|
||||
break;
|
||||
case JVK_PAGE_DOWN:
|
||||
ret = XK_Page_Down;
|
||||
break;
|
||||
case JVK_OPEN_BRACKET:
|
||||
ret = XK_bracketleft;
|
||||
break;
|
||||
case JVK_CLOSE_BRACKET:
|
||||
ret = XK_bracketright;
|
||||
break;
|
||||
case JVK_LEFT_PARENTHESIS:
|
||||
ret = XK_parenleft;
|
||||
break;
|
||||
case JVK_RIGHT_PARENTHESIS:
|
||||
ret = XK_parenright;
|
||||
break;
|
||||
case 0x08:
|
||||
ret = XK_Delete;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Convert X11 keycode to Java ones.
|
||||
* \param keycode X11 keycode
|
||||
* \return Java keycode
|
||||
*/
|
||||
static int convertX11KeycodeToJava(int keycode)
|
||||
{
|
||||
int ret = -1;
|
||||
switch(keycode)
|
||||
{
|
||||
/* 0 - 9 */
|
||||
case XK_0:
|
||||
ret = JVK_0;
|
||||
break;
|
||||
case XK_1:
|
||||
ret = JVK_1;
|
||||
break;
|
||||
case XK_2:
|
||||
ret = JVK_2;
|
||||
break;
|
||||
case XK_3:
|
||||
ret = JVK_3;
|
||||
break;
|
||||
case XK_4:
|
||||
ret = JVK_4;
|
||||
break;
|
||||
case XK_5:
|
||||
ret = JVK_5;
|
||||
break;
|
||||
case XK_6:
|
||||
ret = JVK_6;
|
||||
break;
|
||||
case XK_7:
|
||||
ret = JVK_7;
|
||||
break;
|
||||
case XK_8:
|
||||
ret = JVK_8;
|
||||
break;
|
||||
case XK_9:
|
||||
ret = JVK_9;
|
||||
break;
|
||||
/* A - Z */
|
||||
case XK_A:
|
||||
case XK_a:
|
||||
ret = JVK_A;
|
||||
break;
|
||||
case XK_B:
|
||||
case XK_b:
|
||||
ret = JVK_B;
|
||||
break;
|
||||
case XK_C:
|
||||
case XK_c:
|
||||
ret = JVK_C;
|
||||
break;
|
||||
case XK_D:
|
||||
case XK_d:
|
||||
ret = JVK_D;
|
||||
break;
|
||||
case XK_E:
|
||||
case XK_e:
|
||||
ret = JVK_E;
|
||||
break;
|
||||
case XK_F:
|
||||
case XK_f:
|
||||
ret = JVK_F;
|
||||
break;
|
||||
case XK_G:
|
||||
case XK_g:
|
||||
ret = JVK_G;
|
||||
break;
|
||||
case XK_H:
|
||||
case XK_h:
|
||||
ret = JVK_H;
|
||||
break;
|
||||
case XK_I:
|
||||
case XK_i:
|
||||
ret = JVK_I;
|
||||
break;
|
||||
case XK_J:
|
||||
case XK_j:
|
||||
ret = JVK_J;
|
||||
break;
|
||||
case XK_K:
|
||||
case XK_k:
|
||||
ret = JVK_K;
|
||||
break;
|
||||
case XK_L:
|
||||
case XK_l:
|
||||
ret = JVK_L;
|
||||
break;
|
||||
case XK_M:
|
||||
case XK_m:
|
||||
ret = JVK_M;
|
||||
break;
|
||||
case XK_N:
|
||||
case XK_n:
|
||||
ret = JVK_N;
|
||||
break;
|
||||
case XK_O:
|
||||
case XK_o:
|
||||
ret = JVK_O;
|
||||
break;
|
||||
case XK_P:
|
||||
case XK_p:
|
||||
ret = JVK_P;
|
||||
break;
|
||||
case XK_Q:
|
||||
case XK_q:
|
||||
ret = JVK_Q;
|
||||
break;
|
||||
case XK_R:
|
||||
case XK_r:
|
||||
ret = JVK_R;
|
||||
break;
|
||||
case XK_S:
|
||||
case XK_s:
|
||||
ret = JVK_S;
|
||||
break;
|
||||
case XK_T:
|
||||
case XK_t:
|
||||
ret = JVK_T;
|
||||
break;
|
||||
case XK_U:
|
||||
case XK_u:
|
||||
ret = JVK_U;
|
||||
break;
|
||||
case XK_V:
|
||||
case XK_v:
|
||||
ret = JVK_V;
|
||||
break;
|
||||
case XK_W:
|
||||
case XK_w:
|
||||
ret = JVK_W;
|
||||
break;
|
||||
case XK_X:
|
||||
case XK_x:
|
||||
ret = JVK_X;
|
||||
break;
|
||||
case XK_Y:
|
||||
case XK_y:
|
||||
ret = JVK_Y;
|
||||
break;
|
||||
case XK_Z:
|
||||
case XK_z:
|
||||
ret = JVK_Z;
|
||||
break;
|
||||
/* F1 - F12 */
|
||||
case XK_F1:
|
||||
ret = JVK_F1;
|
||||
break;
|
||||
case XK_F2:
|
||||
ret = JVK_F2;
|
||||
break;
|
||||
case XK_F3:
|
||||
ret = JVK_F3;
|
||||
break;
|
||||
case XK_F4:
|
||||
ret = JVK_F4;
|
||||
break;
|
||||
case XK_F5:
|
||||
ret = JVK_F5;
|
||||
break;
|
||||
case XK_F6:
|
||||
ret = JVK_F6;
|
||||
break;
|
||||
case XK_F7:
|
||||
ret = JVK_F7;
|
||||
break;
|
||||
case XK_F8:
|
||||
ret = JVK_F8;
|
||||
break;
|
||||
case XK_F9:
|
||||
ret = JVK_F9;
|
||||
break;
|
||||
case XK_F10:
|
||||
ret = JVK_F10;
|
||||
break;
|
||||
case XK_F11:
|
||||
ret = JVK_F11;
|
||||
break;
|
||||
case XK_F12:
|
||||
ret = JVK_F12;
|
||||
break;
|
||||
/* arrows (left, right, up, down) */
|
||||
case XK_Left:
|
||||
ret = JVK_LEFT;
|
||||
break;
|
||||
case XK_Right:
|
||||
ret = JVK_RIGHT;
|
||||
break;
|
||||
case XK_Up:
|
||||
ret = JVK_UP;
|
||||
break;
|
||||
case XK_Down:
|
||||
ret = JVK_DOWN;
|
||||
break;
|
||||
case XK_comma:
|
||||
ret = JVK_COMMA;
|
||||
break;
|
||||
case XK_minus:
|
||||
ret = JVK_MINUS;
|
||||
break;
|
||||
case XK_plus:
|
||||
ret = JVK_PLUS;
|
||||
break;
|
||||
case XK_period:
|
||||
ret = JVK_PERIOD;
|
||||
break;
|
||||
case XK_slash:
|
||||
ret = JVK_SLASH;
|
||||
break;
|
||||
case XK_backslash:
|
||||
ret = JVK_BACK_SLASH;
|
||||
break;
|
||||
case XK_semicolon:
|
||||
ret = JVK_SEMICOLON;
|
||||
break;
|
||||
case XK_equal:
|
||||
ret = JVK_EQUALS;
|
||||
break;
|
||||
case XK_colon:
|
||||
ret = JVK_COLON;
|
||||
break;
|
||||
case XK_underscore:
|
||||
ret = JVK_UNDERSCORE;
|
||||
break;
|
||||
case XK_dollar:
|
||||
ret = JVK_DOLLAR;
|
||||
break;
|
||||
case XK_exclam:
|
||||
ret = JVK_EXCLAMATION_MARK;
|
||||
break;
|
||||
case XK_greater:
|
||||
ret = JVK_GREATER;
|
||||
break;
|
||||
case XK_less:
|
||||
ret = JVK_LESS;
|
||||
break;
|
||||
case XK_quoteleft:
|
||||
ret = JVK_QUOTE;
|
||||
break;
|
||||
case XK_quoteright:
|
||||
ret = JVK_BACK_QUOTE;
|
||||
break;
|
||||
case XK_Insert:
|
||||
ret = JVK_INSERT;
|
||||
break;
|
||||
case XK_Help:
|
||||
ret = JVK_HELP;
|
||||
break;
|
||||
case XK_Home:
|
||||
ret = JVK_HOME;
|
||||
break;
|
||||
case XK_End:
|
||||
ret = JVK_END;
|
||||
break;
|
||||
case XK_Page_Up:
|
||||
ret = JVK_PAGE_UP;
|
||||
break;
|
||||
case XK_Page_Down:
|
||||
ret = JVK_PAGE_DOWN;
|
||||
break;
|
||||
case JVK_OPEN_BRACKET:
|
||||
ret = XK_bracketleft;
|
||||
break;
|
||||
case XK_bracketright:
|
||||
ret = JVK_CLOSE_BRACKET;
|
||||
break;
|
||||
case XK_parenleft:
|
||||
ret = JVK_LEFT_PARENTHESIS;
|
||||
break;
|
||||
case XK_parenright:
|
||||
ret = JVK_RIGHT_PARENTHESIS;
|
||||
break;
|
||||
case XK_Delete:
|
||||
ret = 0x08;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Convert X11 modifiers to Java user-defined ones.
|
||||
* \param modfiers X11 modifiers
|
||||
* \return Java user-defined modifiers
|
||||
*/
|
||||
static int X11ModifiersToJavaUserDefined(int modifiers)
|
||||
{
|
||||
int javaModifiers = 0;
|
||||
|
||||
if(modifiers & ControlMask)
|
||||
{
|
||||
javaModifiers |= 0x01;
|
||||
}
|
||||
if(modifiers & Mod1Mask)
|
||||
{
|
||||
/* Alt */
|
||||
javaModifiers |= 0x02;
|
||||
}
|
||||
if(modifiers & ShiftMask)
|
||||
{
|
||||
javaModifiers |= 0x04;
|
||||
}
|
||||
if(modifiers & Mod4Mask)
|
||||
{
|
||||
/* Super */
|
||||
javaModifiers |= 0x08;
|
||||
}
|
||||
|
||||
return javaModifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Convert Java user-defined modifiers to X11 ones.
|
||||
* \param modifiers Java user-defined modifiers
|
||||
* \return X11 modifiers
|
||||
*/
|
||||
static int JavaUserDefinedModifiersToX11(int modifiers)
|
||||
{
|
||||
int x11Modifiers = 0;
|
||||
|
||||
if(modifiers & 0x01)
|
||||
{
|
||||
x11Modifiers |= ControlMask;
|
||||
}
|
||||
if(modifiers & 0x02)
|
||||
{
|
||||
/* Alt */
|
||||
x11Modifiers |= Mod1Mask;
|
||||
}
|
||||
if(modifiers & 0x04)
|
||||
{
|
||||
x11Modifiers |= ShiftMask;
|
||||
}
|
||||
if(modifiers & 0x08)
|
||||
{
|
||||
/* Super */
|
||||
x11Modifiers |= Mod4Mask;
|
||||
}
|
||||
|
||||
return x11Modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Notify Java side about key pressed (keycode + modifiers).
|
||||
* \param keycode keycode
|
||||
* \param modifiers modifiers used (SHIFT, CTRL, ALT, LOGO)
|
||||
*/
|
||||
static void notify(struct keyboard_hook* keyboard, jint keycode, jint modifiers)
|
||||
{
|
||||
JNIEnv *jniEnv = NULL;
|
||||
jclass delegateClass = NULL;
|
||||
|
||||
if(!keyboard->delegate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(0 != keyboard->jvm->AttachCurrentThreadAsDaemon((void **)&jniEnv, NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
delegateClass = jniEnv->GetObjectClass(keyboard->delegate);
|
||||
|
||||
if(delegateClass)
|
||||
{
|
||||
jmethodID methodid = NULL;
|
||||
|
||||
methodid = jniEnv->GetMethodID(delegateClass, "receiveKey", "(II)V");
|
||||
|
||||
if(methodid)
|
||||
{
|
||||
jniEnv->CallVoidMethod(keyboard->delegate, methodid, keycode, modifiers);
|
||||
}
|
||||
}
|
||||
jniEnv->ExceptionClear();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief X11 event loop thread entry point.
|
||||
* \param arg thread argument
|
||||
* \return NULL
|
||||
*/
|
||||
static void* x11_event_loop_thread(void* arg)
|
||||
{
|
||||
struct keyboard_hook* keyboard = (struct keyboard_hook*)arg;
|
||||
|
||||
XSelectInput(keyboard->display, keyboard->root, KeyPressMask);
|
||||
|
||||
while(keyboard->running)
|
||||
{
|
||||
XEvent ev;
|
||||
while(XCheckMaskEvent(keyboard->display, 0xFFFFFFFF, &ev))
|
||||
{
|
||||
switch (ev.type)
|
||||
{
|
||||
case KeyPress:
|
||||
for(std::list<keystrok>::iterator it = keyboard->keystrokes.begin() ; it != keyboard->keystrokes.end() ; ++it)
|
||||
{
|
||||
keystrok& ks = (*it);
|
||||
XKeyEvent* keyEvent = (XKeyEvent*)&ev.xkey;
|
||||
unsigned long keycode = -1;
|
||||
//XKeycodeToKeysym(keyboard->display, keyEvent->keycode, 1);
|
||||
|
||||
XLookupString(keyEvent, NULL, 0, &keycode, NULL);
|
||||
|
||||
keycode = convertX11KeycodeToJava(keycode);
|
||||
int modifiers = X11ModifiersToJavaUserDefined(keyEvent->state);
|
||||
|
||||
if(ks.vkcode == keycode && ks.modifiers == modifiers)
|
||||
{
|
||||
notify(keyboard, ks.vkcode, ks.modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(std::list<keystrok>::iterator it = keyboard->keystrokes.begin() ; it != keyboard->keystrokes.end() ; ++it)
|
||||
{
|
||||
keystrok& ks = (*it);
|
||||
|
||||
if(ks.active == 0)
|
||||
{
|
||||
/* hotkey to add */
|
||||
int x11Keycode = convertJavaKeycodeToX11(ks.vkcode);
|
||||
if(x11Keycode != -1)
|
||||
{
|
||||
x11Keycode = XKeysymToKeycode(keyboard->display, x11Keycode);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("failed\n");fflush(stdout);
|
||||
ks.active = -1;
|
||||
continue;
|
||||
}
|
||||
int x11Modifiers = JavaUserDefinedModifiersToX11(ks.modifiers);
|
||||
|
||||
ks.active = 1;
|
||||
if(XGrabKey(keyboard->display, x11Keycode, x11Modifiers, keyboard->root, False, GrabModeAsync, GrabModeAsync) > 1)
|
||||
{
|
||||
fprintf(stderr, "[LOOP] Error when XGrabKey\n");fflush(stderr);
|
||||
ks.active = -1;
|
||||
}
|
||||
}
|
||||
else if(ks.active == -1)
|
||||
{
|
||||
/* hotkey to remove */
|
||||
int x11Keycode = XKeysymToKeycode(keyboard->display,
|
||||
convertJavaKeycodeToX11(ks.vkcode));
|
||||
int x11Modifiers = JavaUserDefinedModifiersToX11(ks.modifiers);
|
||||
|
||||
if(XUngrabKey(keyboard->display, x11Keycode, x11Modifiers, keyboard->root) > 1)
|
||||
{
|
||||
fprintf(stderr, "[LOOP] Error when XUngrabKey\n");fflush(stderr);
|
||||
}
|
||||
it = keyboard->keystrokes.erase(it)--;
|
||||
}
|
||||
}
|
||||
|
||||
usleep(1000 * 1000);
|
||||
pthread_yield();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_init
|
||||
(JNIEnv* jniEnv, jclass clazz)
|
||||
{
|
||||
struct keyboard_hook* keyboard = NULL;
|
||||
|
||||
(void)jniEnv;
|
||||
(void)clazz;
|
||||
|
||||
keyboard = new keyboard_hook();
|
||||
if(!keyboard)
|
||||
{
|
||||
return (jlong)NULL;
|
||||
}
|
||||
|
||||
keyboard->display = XOpenDisplay(NULL);
|
||||
|
||||
if(!keyboard->display)
|
||||
{
|
||||
free(keyboard);
|
||||
return (jlong)NULL;
|
||||
}
|
||||
|
||||
keyboard->root = DefaultRootWindow(keyboard->display);
|
||||
|
||||
return (jlong)keyboard;
|
||||
}
|
||||
|
||||
/* XXX release JNI method */
|
||||
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_start
|
||||
(JNIEnv* jniEnv, jclass clazz, jlong ptr)
|
||||
{
|
||||
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
|
||||
pthread_t id;
|
||||
pthread_attr_t attr;
|
||||
|
||||
(void)jniEnv;
|
||||
(void)clazz;
|
||||
|
||||
if(keyboard->running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
|
||||
if(pthread_attr_setdetachstate(&attr, 1) != 0)
|
||||
{
|
||||
perror("pthread_attr_setdetachstate");fflush(stderr);
|
||||
return;
|
||||
}
|
||||
|
||||
keyboard->running = 1;
|
||||
|
||||
if(pthread_create(&id, &attr, x11_event_loop_thread, keyboard) != 0)
|
||||
{
|
||||
perror("pthread_create");fflush(stderr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_stop
|
||||
(JNIEnv* jniEnv, jclass clazz, jlong ptr)
|
||||
{
|
||||
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
|
||||
|
||||
(void)jniEnv;
|
||||
(void)clazz;
|
||||
|
||||
keyboard->running = 0;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_setDelegate
|
||||
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jobject delegate)
|
||||
{
|
||||
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
|
||||
|
||||
(void)clazz;
|
||||
|
||||
if(keyboard->delegate)
|
||||
{
|
||||
jniEnv->DeleteGlobalRef(keyboard->delegate);
|
||||
keyboard->delegate = NULL;
|
||||
}
|
||||
|
||||
if(delegate)
|
||||
{
|
||||
jobject delegate2 = jniEnv->NewGlobalRef(delegate);
|
||||
if(delegate2)
|
||||
{
|
||||
jniEnv->GetJavaVM(&keyboard->jvm);
|
||||
keyboard->delegate = delegate2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_registerShortcut
|
||||
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode, jint modifiers)
|
||||
{
|
||||
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
|
||||
|
||||
(void)jniEnv;
|
||||
(void)clazz;
|
||||
|
||||
if(keyboard)
|
||||
{
|
||||
keystrok ks;
|
||||
|
||||
ks.vkcode = keycode;
|
||||
ks.modifiers = modifiers;
|
||||
ks.active = 0;
|
||||
keyboard->keystrokes.push_back(ks);
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterShortcut
|
||||
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode, jint modifiers)
|
||||
{
|
||||
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
|
||||
|
||||
(void)jniEnv;
|
||||
(void)clazz;
|
||||
|
||||
if(keyboard)
|
||||
{
|
||||
for(std::list<keystrok>::iterator it = keyboard->keystrokes.begin() ; it != keyboard->keystrokes.end() ; ++it)
|
||||
{
|
||||
keystrok& ks = (*it);
|
||||
if(ks.vkcode == keycode && ks.modifiers == modifiers)
|
||||
{
|
||||
ks.active = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,61 @@
|
||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook */
|
||||
|
||||
#ifndef _Included_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
|
||||
#define _Included_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
|
||||
* Method: init
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_init
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
/*
|
||||
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
|
||||
* Method: start
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_start
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
/*
|
||||
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
|
||||
* Method: stop
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_stop
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
/*
|
||||
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
|
||||
* Method: setDelegate
|
||||
* Signature: (JLnet/java/sip/communicator/impl/globalshortcut/NativeKeyboardHookDelegate;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_setDelegate
|
||||
(JNIEnv *, jclass, jlong, jobject);
|
||||
|
||||
/*
|
||||
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
|
||||
* Method: registerShortcut
|
||||
* Signature: (JII)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_registerShortcut
|
||||
(JNIEnv *, jclass, jlong, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
|
||||
* Method: unregisterShortcut
|
||||
* Signature: (JII)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterShortcut
|
||||
(JNIEnv *, jclass, jlong, jint, jint);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@ -0,0 +1,940 @@
|
||||
/*
|
||||
* Jitsi Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
/bin/bash: 2: command not found
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
|
||||
#include "net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook.h"
|
||||
|
||||
/* Mac OS X specific code */
|
||||
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
#import "DDHotKeyCenter.h"
|
||||
#import "javakey.h"
|
||||
|
||||
/* following enum comes from http://snipplr.com/view/42797/ */
|
||||
enum
|
||||
{
|
||||
kVK_ANSI_A = 0x00,
|
||||
kVK_ANSI_S = 0x01,
|
||||
kVK_ANSI_D = 0x02,
|
||||
kVK_ANSI_F = 0x03,
|
||||
kVK_ANSI_H = 0x04,
|
||||
kVK_ANSI_G = 0x05,
|
||||
kVK_ANSI_Z = 0x06,
|
||||
kVK_ANSI_X = 0x07,
|
||||
kVK_ANSI_C = 0x08,
|
||||
kVK_ANSI_V = 0x09,
|
||||
kVK_ANSI_B = 0x0B,
|
||||
kVK_ANSI_Q = 0x0C,
|
||||
kVK_ANSI_W = 0x0D,
|
||||
kVK_ANSI_E = 0x0E,
|
||||
kVK_ANSI_R = 0x0F,
|
||||
kVK_ANSI_Y = 0x10,
|
||||
kVK_ANSI_T = 0x11,
|
||||
kVK_ANSI_1 = 0x12,
|
||||
kVK_ANSI_2 = 0x13,
|
||||
kVK_ANSI_3 = 0x14,
|
||||
kVK_ANSI_4 = 0x15,
|
||||
kVK_ANSI_6 = 0x16,
|
||||
kVK_ANSI_5 = 0x17,
|
||||
kVK_ANSI_Equal = 0x18,
|
||||
kVK_ANSI_9 = 0x19,
|
||||
kVK_ANSI_7 = 0x1A,
|
||||
kVK_ANSI_Minus = 0x1B,
|
||||
kVK_ANSI_8 = 0x1C,
|
||||
kVK_ANSI_0 = 0x1D,
|
||||
kVK_ANSI_RightBracket = 0x1E,
|
||||
kVK_ANSI_O = 0x1F,
|
||||
kVK_ANSI_U = 0x20,
|
||||
kVK_ANSI_LeftBracket = 0x21,
|
||||
kVK_ANSI_I = 0x22,
|
||||
kVK_ANSI_P = 0x23,
|
||||
kVK_ANSI_L = 0x25,
|
||||
kVK_ANSI_J = 0x26,
|
||||
kVK_ANSI_Quote = 0x27,
|
||||
kVK_ANSI_K = 0x28,
|
||||
kVK_ANSI_Semicolon = 0x29,
|
||||
kVK_ANSI_Backslash = 0x2A,
|
||||
kVK_ANSI_Comma = 0x2B,
|
||||
kVK_ANSI_Slash = 0x2C,
|
||||
kVK_ANSI_N = 0x2D,
|
||||
kVK_ANSI_M = 0x2E,
|
||||
kVK_ANSI_Period = 0x2F,
|
||||
kVK_ANSI_Grave =JVK_2,
|
||||
kVK_ANSI_KeypadDecimal = 0x41,
|
||||
kVK_ANSI_KeypadMultiply = 0x43,
|
||||
kVK_ANSI_KeypadPlus = 0x45,
|
||||
kVK_ANSI_KeypadClear = 0x47,
|
||||
kVK_ANSI_KeypadDivide = 0x4B,
|
||||
kVK_ANSI_KeypadEnter = 0x4C,
|
||||
kVK_ANSI_KeypadMinus = 0x4E,
|
||||
kVK_ANSI_KeypadEquals = 0x51,
|
||||
kVK_ANSI_Keypad0 = 0x52,
|
||||
kVK_ANSI_Keypad1 = 0x53,
|
||||
kVK_ANSI_Keypad2 = 0x54,
|
||||
kVK_ANSI_Keypad3 = 0x55,
|
||||
kVK_ANSI_Keypad4 = 0x56,
|
||||
kVK_ANSI_Keypad5 = 0x57,
|
||||
kVK_ANSI_Keypad6 = 0x58,
|
||||
kVK_ANSI_Keypad7 = 0x59,
|
||||
kVK_ANSI_Keypad8 = 0x5B,
|
||||
kVK_ANSI_Keypad9 = 0x5C
|
||||
};
|
||||
|
||||
/* keycodes for keys that are independent of keyboard layout*/
|
||||
enum
|
||||
{
|
||||
kVK_Return = 0x24,
|
||||
kVK_Tab =JVK_0,
|
||||
kVK_Space =JVK_1,
|
||||
kVK_Delete =JVK_3,
|
||||
kVK_Escape =JVK_5,
|
||||
kVK_Command =JVK_7,
|
||||
kVK_Shift =JVK_8,
|
||||
kVK_CapsLock =JVK_9,
|
||||
kVK_Option =JVK_A,
|
||||
kVK_Control =JVK_B,
|
||||
kVK_RightShift =JVK_C,
|
||||
kVK_RightOption =JVK_D,
|
||||
kVK_RightControl =JVK_E,
|
||||
kVK_Function =JVK_F,
|
||||
kVK_F17 = 0x40,
|
||||
kVK_VolumeUp = 0x48,
|
||||
kVK_VolumeDown = 0x49,
|
||||
kVK_Mute = 0x4A,
|
||||
kVK_F18 = 0x4F,
|
||||
kVK_F19 = 0x50,
|
||||
kVK_F20 = 0x5A,
|
||||
kVK_F5 = 0x60,
|
||||
kVK_F6 = 0x61,
|
||||
kVK_F7 = 0x62,
|
||||
kVK_F3 = 0x63,
|
||||
kVK_F8 = 0x64,
|
||||
kVK_F9 = 0x65,
|
||||
kVK_F11 = 0x67,
|
||||
kVK_F13 = 0x69,
|
||||
kVK_F16 = 0x6A,
|
||||
kVK_F14 = 0x6B,
|
||||
kVK_F10 = 0x6D,
|
||||
kVK_F12 = 0x6F,
|
||||
kVK_F15 = 0x71,
|
||||
kVK_Help = 0x72,
|
||||
kVK_Home = 0x73,
|
||||
kVK_PageUp = 0x74,
|
||||
kVK_ForwardDelete = 0x75,
|
||||
kVK_F4 = 0x76,
|
||||
kVK_End = 0x77,
|
||||
kVK_F2 = 0x78,
|
||||
kVK_PageDown = 0x79,
|
||||
kVK_F1 = 0x7A,
|
||||
kVK_LeftArrow = 0x7B,
|
||||
kVK_RightArrow = 0x7C,
|
||||
kVK_DownArrow = 0x7D,
|
||||
kVK_UpArrow = 0x7E
|
||||
};
|
||||
|
||||
/* ISO keyboards only*/
|
||||
enum
|
||||
{
|
||||
kVK_ISO_Section = 0x0A
|
||||
};
|
||||
|
||||
/* JIS keyboards only*/
|
||||
enum
|
||||
{
|
||||
kVK_JIS_Yen = 0x5D,
|
||||
kVK_JIS_Underscore = 0x5E,
|
||||
kVK_JIS_KeypadComma = 0x5F,
|
||||
kVK_JIS_Eisu = 0x66,
|
||||
kVK_JIS_Kana = 0x68
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Convert Java keycode to native ones.
|
||||
* Reference: http://boredzo.org/blog/wp-content/uploads/2007/05/imtx-virtual-keycodes.png
|
||||
* \param keycode Java keycode
|
||||
* \return native Mac OS X keycode
|
||||
*/
|
||||
static int convertJavaKeycodeToMac(int keycode)
|
||||
{
|
||||
int ret = -1;
|
||||
switch(keycode)
|
||||
{
|
||||
/* 0 - 9 */
|
||||
case JVK_0:
|
||||
ret = kVK_ANSI_0;
|
||||
break;
|
||||
case JVK_1:
|
||||
ret = kVK_ANSI_1;
|
||||
break;
|
||||
case JVK_2:
|
||||
ret = kVK_ANSI_2;
|
||||
break;
|
||||
case JVK_3:
|
||||
ret = kVK_ANSI_3;
|
||||
break;
|
||||
case JVK_4:
|
||||
ret = kVK_ANSI_4;
|
||||
break;
|
||||
case JVK_5:
|
||||
ret = kVK_ANSI_5;
|
||||
break;
|
||||
case JVK_6:
|
||||
ret = kVK_ANSI_6;
|
||||
break;
|
||||
case JVK_7:
|
||||
ret = kVK_ANSI_7;
|
||||
break;
|
||||
case JVK_8:
|
||||
ret = kVK_ANSI_8;
|
||||
break;
|
||||
case JVK_9:
|
||||
ret = kVK_ANSI_9;
|
||||
break;
|
||||
/* A - Z */
|
||||
case JVK_A:
|
||||
ret = kVK_ANSI_A;
|
||||
break;
|
||||
case JVK_B:
|
||||
ret = kVK_ANSI_B;
|
||||
break;
|
||||
case JVK_C:
|
||||
ret = kVK_ANSI_C;
|
||||
break;
|
||||
case JVK_D:
|
||||
ret = kVK_ANSI_D;
|
||||
break;
|
||||
case JVK_E:
|
||||
ret = kVK_ANSI_E;
|
||||
break;
|
||||
case JVK_F:
|
||||
ret = kVK_ANSI_F;
|
||||
break;
|
||||
case JVK_G:
|
||||
ret = kVK_ANSI_G;
|
||||
break;
|
||||
case JVK_H:
|
||||
ret = kVK_ANSI_H;
|
||||
break;
|
||||
case JVK_I:
|
||||
ret = kVK_ANSI_I;
|
||||
break;
|
||||
case JVK_J:
|
||||
ret = kVK_ANSI_J;
|
||||
break;
|
||||
case JVK_K:
|
||||
ret = kVK_ANSI_K;
|
||||
break;
|
||||
case JVK_L:
|
||||
ret = kVK_ANSI_L;
|
||||
break;
|
||||
case JVK_M:
|
||||
ret = kVK_ANSI_M;
|
||||
break;
|
||||
case JVK_N:
|
||||
ret = kVK_ANSI_N;
|
||||
break;
|
||||
case JVK_O:
|
||||
ret = kVK_ANSI_O;
|
||||
break;
|
||||
case JVK_P:
|
||||
ret = kVK_ANSI_P;
|
||||
break;
|
||||
case JVK_Q:
|
||||
ret = kVK_ANSI_Q;
|
||||
break;
|
||||
case JVK_R:
|
||||
ret = kVK_ANSI_R;
|
||||
break;
|
||||
case JVK_S:
|
||||
ret = kVK_ANSI_S;
|
||||
break;
|
||||
case JVK_T:
|
||||
ret = kVK_ANSI_T;
|
||||
break;
|
||||
case JVK_U:
|
||||
ret = kVK_ANSI_U;
|
||||
break;
|
||||
case JVK_V:
|
||||
ret = kVK_ANSI_V;
|
||||
break;
|
||||
case JVK_W:
|
||||
ret = kVK_ANSI_W;
|
||||
break;
|
||||
case JVK_X:
|
||||
ret = kVK_ANSI_X;
|
||||
break;
|
||||
case JVK_Y:
|
||||
ret = kVK_ANSI_Y;
|
||||
break;
|
||||
case JVK_Z:
|
||||
ret = kVK_ANSI_Z;
|
||||
break;
|
||||
/* F1 - F12 */
|
||||
case JVK_F1:
|
||||
ret = kVK_F1;
|
||||
break;
|
||||
case JVK_F2:
|
||||
ret = kVK_F2;
|
||||
break;
|
||||
case JVK_F3:
|
||||
ret = kVK_F3;
|
||||
break;
|
||||
case JVK_F4:
|
||||
ret = kVK_F4;
|
||||
break;
|
||||
case JVK_F5:
|
||||
ret = kVK_F5;
|
||||
break;
|
||||
case JVK_F6:
|
||||
ret = kVK_F6;
|
||||
break;
|
||||
case JVK_F7:
|
||||
ret = kVK_F7;
|
||||
break;
|
||||
case JVK_F8:
|
||||
ret = kVK_F8;
|
||||
break;
|
||||
case JVK_F9:
|
||||
ret = kVK_F9;
|
||||
break;
|
||||
case JVK_F10:
|
||||
ret = kVK_F10;
|
||||
break;
|
||||
case JVK_F11:
|
||||
ret = kVK_F11;
|
||||
break;
|
||||
case JVK_F12:
|
||||
ret = kVK_F12;
|
||||
break;
|
||||
/* arrows (left, right, up, down) */
|
||||
case JVK_LEFT:
|
||||
ret = kVK_LeftArrow;
|
||||
break;
|
||||
case JVK_RIGHT:
|
||||
ret = kVK_RightArrow;
|
||||
break;
|
||||
case JVK_UP:
|
||||
ret = kVK_UpArrow;
|
||||
break;
|
||||
case JVK_DOWN:
|
||||
ret = kVK_DownArrow;
|
||||
break;
|
||||
case JVK_COMMA:
|
||||
ret = kVK_ANSI_Comma;
|
||||
break;
|
||||
case JVK_MINUS:
|
||||
ret = kVK_ANSI_Minus;
|
||||
break;
|
||||
case JVK_PLUS:
|
||||
ret = kVK_ANSI_KeypadPlus;
|
||||
break;
|
||||
case JVK_PERIOD:
|
||||
ret = kVK_ANSI_Period;
|
||||
break;
|
||||
case JVK_SLASH:
|
||||
ret = kVK_ANSI_Slash;
|
||||
break;
|
||||
case JVK_BACK_SLASH:
|
||||
ret = kVK_ANSI_Backslash;
|
||||
break;
|
||||
case JVK_SEMICOLON:
|
||||
ret = kVK_ANSI_Semicolon;
|
||||
break;
|
||||
case JVK_EQUALS:
|
||||
ret = kVK_ANSI_Equal;
|
||||
break;
|
||||
case JVK_COLON:
|
||||
ret = -1;
|
||||
break;
|
||||
case JVK_UNDERSCORE:
|
||||
ret = kVK_JIS_Underscore;
|
||||
break;
|
||||
case JVK_DOLLAR:
|
||||
ret = -1;
|
||||
break;
|
||||
case JVK_EXCLAMATION_MARK:
|
||||
ret = -1;
|
||||
break;
|
||||
case JVK_GREATER:
|
||||
ret = -1;
|
||||
break;
|
||||
case JVK_LESS:
|
||||
ret = -1;
|
||||
break;
|
||||
case JVK_QUOTE:
|
||||
ret = kVK_ANSI_Quote;
|
||||
break;
|
||||
case JVK_BACK_QUOTE:
|
||||
ret = -1;
|
||||
break;
|
||||
case JVK_INSERT:
|
||||
ret = -1;
|
||||
break;
|
||||
case JVK_HELP:
|
||||
ret = kVK_Help;
|
||||
break;
|
||||
case JVK_HOME:
|
||||
ret = kVK_Home;
|
||||
break;
|
||||
case JVK_END:
|
||||
ret = kVK_End;
|
||||
break;
|
||||
case JVK_PAGE_UP:
|
||||
ret = kVK_PageUp;
|
||||
break;
|
||||
case JVK_PAGE_DOWN:
|
||||
ret = kVK_PageDown;
|
||||
break;
|
||||
case JVK_OPEN_BRACKET:
|
||||
ret = kVK_ANSI_LeftBracket;
|
||||
break;
|
||||
case JVK_CLOSE_BRACKET:
|
||||
ret = kVK_ANSI_RightBracket;
|
||||
break;
|
||||
case 0x08:
|
||||
ret = kVK_Delete;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Convert Mac OS X keycode to Java ones.
|
||||
* Reference: http://boredzo.org/blog/wp-content/uploads/2007/05/imtx-virtual-keycodes.png
|
||||
* \param keycode Mac OS X keycode
|
||||
* \return Java keycode
|
||||
*/
|
||||
static int convertMacKeycodeToJava(int keycode)
|
||||
{
|
||||
int ret = -1;
|
||||
switch(keycode)
|
||||
{
|
||||
/* 0 - 9 */
|
||||
case kVK_ANSI_0:
|
||||
ret =JVK_0;
|
||||
break;
|
||||
case kVK_ANSI_1:
|
||||
ret =JVK_1;
|
||||
break;
|
||||
case kVK_ANSI_2:
|
||||
ret =JVK_2;
|
||||
break;
|
||||
case kVK_ANSI_3:
|
||||
ret =JVK_3;
|
||||
break;
|
||||
case kVK_ANSI_4:
|
||||
ret =JVK_4;
|
||||
break;
|
||||
case kVK_ANSI_5:
|
||||
ret =JVK_5;
|
||||
break;
|
||||
case kVK_ANSI_6:
|
||||
ret =JVK_6;
|
||||
break;
|
||||
case kVK_ANSI_7:
|
||||
ret =JVK_7;
|
||||
break;
|
||||
case kVK_ANSI_8:
|
||||
ret =JVK_8;
|
||||
break;
|
||||
case kVK_ANSI_9:
|
||||
ret =JVK_9;
|
||||
break;
|
||||
/* A - Z */
|
||||
case kVK_ANSI_A:
|
||||
ret =JVK_A;
|
||||
break;
|
||||
case kVK_ANSI_B:
|
||||
ret =JVK_B;
|
||||
break;
|
||||
case kVK_ANSI_C:
|
||||
ret =JVK_C;
|
||||
break;
|
||||
case kVK_ANSI_D:
|
||||
ret =JVK_D;
|
||||
break;
|
||||
case kVK_ANSI_E:
|
||||
ret =JVK_E;
|
||||
break;
|
||||
case kVK_ANSI_F:
|
||||
ret =JVK_F;
|
||||
break;
|
||||
case kVK_ANSI_G:
|
||||
ret =JVK_G;
|
||||
break;
|
||||
case kVK_ANSI_H:
|
||||
ret =JVK_H;
|
||||
break;
|
||||
case kVK_ANSI_I:
|
||||
ret =JVK_I;
|
||||
break;
|
||||
case kVK_ANSI_J:
|
||||
ret =JVK_J;
|
||||
break;
|
||||
case kVK_ANSI_K:
|
||||
ret =JVK_K;
|
||||
break;
|
||||
case kVK_ANSI_L:
|
||||
ret =JVK_L;
|
||||
break;
|
||||
case kVK_ANSI_M:
|
||||
ret =JVK_M;
|
||||
break;
|
||||
case kVK_ANSI_N:
|
||||
ret =JVK_N;
|
||||
break;
|
||||
case kVK_ANSI_O:
|
||||
ret =JVK_O;
|
||||
break;
|
||||
case kVK_ANSI_P:
|
||||
ret =JVK_P;
|
||||
break;
|
||||
case kVK_ANSI_Q:
|
||||
ret =JVK_Q;
|
||||
break;
|
||||
case kVK_ANSI_R:
|
||||
ret =JVK_R;
|
||||
break;
|
||||
case kVK_ANSI_S:
|
||||
ret =JVK_S;
|
||||
break;
|
||||
case kVK_ANSI_T:
|
||||
ret =JVK_T;
|
||||
break;
|
||||
case kVK_ANSI_U:
|
||||
ret =JVK_U;
|
||||
break;
|
||||
case kVK_ANSI_V:
|
||||
ret =JVK_V;
|
||||
break;
|
||||
case kVK_ANSI_W:
|
||||
ret =JVK_W;
|
||||
break;
|
||||
case kVK_ANSI_X:
|
||||
ret =JVK_X;
|
||||
break;
|
||||
case kVK_ANSI_Y:
|
||||
ret =JVK_Y;
|
||||
break;
|
||||
case kVK_ANSI_Z:
|
||||
ret =JVK_Z;
|
||||
break;
|
||||
/* F1 - F12 */
|
||||
case kVK_F1:
|
||||
ret =JVK_F1;
|
||||
break;
|
||||
case kVK_F2:
|
||||
ret =JVK_F2;
|
||||
break;
|
||||
case kVK_F3:
|
||||
ret =JVK_F3;
|
||||
break;
|
||||
case kVK_F4:
|
||||
ret =JVK_F4;
|
||||
break;
|
||||
case kVK_F5:
|
||||
ret =JVK_F5;
|
||||
break;
|
||||
case kVK_F6:
|
||||
ret =JVK_F6;
|
||||
break;
|
||||
case kVK_F7:
|
||||
ret =JVK_F7;
|
||||
break;
|
||||
case kVK_F8:
|
||||
ret =JVK_F8;
|
||||
break;
|
||||
case kVK_F9:
|
||||
ret =JVK_F9;
|
||||
break;
|
||||
case kVK_F10:
|
||||
ret =JVK_F10;
|
||||
break;
|
||||
case kVK_F11:
|
||||
ret =JVK_F11;
|
||||
break;
|
||||
case kVK_F12:
|
||||
ret =JVK_F12;
|
||||
break;
|
||||
/* arrows (left, right, up, down) */
|
||||
case kVK_LeftArrow:
|
||||
ret =JVK_LEFT;
|
||||
break;
|
||||
case kVK_RightArrow:
|
||||
ret =JVK_RIGHT;
|
||||
break;
|
||||
case kVK_UpArrow:
|
||||
ret =JVK_UP;
|
||||
break;
|
||||
case kVK_DownArrow:
|
||||
ret =JVK_DOWN;
|
||||
break;
|
||||
case kVK_ANSI_Comma:
|
||||
ret =JVK_COMMA;
|
||||
break;
|
||||
case kVK_ANSI_Minus:
|
||||
ret =JVK_MINUS;
|
||||
break;
|
||||
case kVK_ANSI_KeypadPlus:
|
||||
ret =JVK_PLUS;
|
||||
break;
|
||||
case kVK_ANSI_Period:
|
||||
ret =JVK_PERIOD;
|
||||
break;
|
||||
case kVK_ANSI_Slash:
|
||||
ret =JVK_SLASH;
|
||||
break;
|
||||
case kVK_ANSI_Backslash:
|
||||
ret =JVK_BACK_SLASH;
|
||||
break;
|
||||
case kVK_ANSI_Semicolon:
|
||||
ret =JVK_SEMICOLON;
|
||||
break;
|
||||
case kVK_ANSI_Equal:
|
||||
ret =JVK_EQUALS;
|
||||
break;
|
||||
case kVK_JIS_Underscore:
|
||||
ret =JVK_UNDERSCORE;
|
||||
break;
|
||||
case kVK_ANSI_Quote:
|
||||
ret =JVK_QUOTE;
|
||||
break;
|
||||
case kVK_Help:
|
||||
ret =JVK_HELP;
|
||||
break;
|
||||
case kVK_Home:
|
||||
ret =JVK_HOME;
|
||||
break;
|
||||
case kVK_End:
|
||||
ret =JVK_END;
|
||||
break;
|
||||
case kVK_PageUp:
|
||||
ret =JVK_PAGE_UP;
|
||||
break;
|
||||
case kVK_PageDown:
|
||||
ret =JVK_PAGE_DOWN;
|
||||
break;
|
||||
case kVK_ANSI_LeftBracket:
|
||||
ret =JVK_OPEN_BRACKET;
|
||||
break;
|
||||
case kVK_ANSI_RightBracket:
|
||||
ret =JVK_CLOSE_BRACKET;
|
||||
break;
|
||||
case kVK_Delete:
|
||||
ret = 0x08;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Convert Java user-defined modifiers to Mac OS X ones.
|
||||
* \param modifiers Java user-defined modifiers
|
||||
* \return Mac OS X modifiers
|
||||
*/
|
||||
static int convertJavaUserDefinedModifiersToMac(int modifiers)
|
||||
{
|
||||
int macModifiers = 0;
|
||||
|
||||
if(modifiers & 0x01)
|
||||
{
|
||||
/* CTRL */
|
||||
macModifiers |= NSControlKeyMask;
|
||||
}
|
||||
if(modifiers & 0x02)
|
||||
{
|
||||
/* ALT */
|
||||
macModifiers |= NSAlternateKeyMask;
|
||||
}
|
||||
if(modifiers & 0x04)
|
||||
{
|
||||
/* SHIFT */
|
||||
macModifiers |= NSShiftKeyMask;
|
||||
}
|
||||
if(modifiers & 0x08)
|
||||
{
|
||||
/* LOGO */
|
||||
macModifiers |= NSCommandKeyMask;
|
||||
}
|
||||
|
||||
return macModifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Convert Mac modifiers to our Java user-defined ones.
|
||||
* \param modifiers Mac modifiers (MOD_CONTROL, ...)
|
||||
* \return Java user-defined modifiers
|
||||
*/
|
||||
static int convertMacModifiersToJavaUserDefined(int modifiers)
|
||||
{
|
||||
int javaModifiers = 0;
|
||||
|
||||
if(modifiers & NSControlKeyMask)
|
||||
{
|
||||
javaModifiers |= 0x01;
|
||||
}
|
||||
if(modifiers & NSAlternateKeyMask)
|
||||
{
|
||||
javaModifiers |= 0x02;
|
||||
}
|
||||
if(modifiers & NSShiftKeyMask)
|
||||
{
|
||||
javaModifiers |= 0x04;
|
||||
}
|
||||
if(modifiers & NSCommandKeyMask)
|
||||
{
|
||||
javaModifiers |= 0x08;
|
||||
}
|
||||
|
||||
return javaModifiers;
|
||||
}
|
||||
|
||||
@interface KeyboardHook: NSObject
|
||||
{
|
||||
@private
|
||||
jobject delegateObject;
|
||||
JavaVM* vm;
|
||||
}
|
||||
|
||||
-(void)dealloc;
|
||||
-(id)init;
|
||||
-(void) setDelegate:(jobject)delegate inJNIEnv:(JNIEnv* )jniEnv;
|
||||
-(void) hotkeyAction:(NSEvent*)hotKeyEvent inObject:(id)anObject;
|
||||
-(void) notify:(int) keyCode inModifiers:(int)modifiers;
|
||||
-(int) registerKey:(int)keycode inModifiers:(int)modifiers;
|
||||
-(void) unregisterKey:(int)keycode inModifiers:(int)modifiers;
|
||||
@end
|
||||
|
||||
@implementation KeyboardHook
|
||||
-(void) notify:(int) keyCode inModifiers:(int)modifiers;
|
||||
{
|
||||
jobject delegate;
|
||||
JNIEnv* jniEnv = NULL;
|
||||
jclass delegateClass = NULL;
|
||||
|
||||
delegate = self->delegateObject;
|
||||
if(!delegate)
|
||||
return;
|
||||
|
||||
vm = self->vm;
|
||||
if(0 != (*vm)->AttachCurrentThreadAsDaemon(vm, (void**)&jniEnv, NULL))
|
||||
return;
|
||||
|
||||
delegateClass = (*jniEnv)->GetObjectClass(jniEnv, delegate);
|
||||
if(delegateClass)
|
||||
{
|
||||
jmethodID methodid = NULL;
|
||||
|
||||
methodid = (*jniEnv)->GetMethodID(jniEnv, delegateClass,"receiveKey", "(II)V");
|
||||
if(methodid)
|
||||
{
|
||||
(*jniEnv)->CallVoidMethod(jniEnv, delegate, methodid, keyCode, modifiers);
|
||||
}
|
||||
}
|
||||
(*jniEnv)->ExceptionClear(jniEnv);
|
||||
}
|
||||
|
||||
- (void)setDelegate:(jobject) delegate inJNIEnv:(JNIEnv*)jniEnv
|
||||
{
|
||||
if(self->delegateObject)
|
||||
{
|
||||
if(!jniEnv)
|
||||
(*(self->vm))->AttachCurrentThread(self->vm, (void**)&jniEnv, NULL);
|
||||
(*jniEnv)->DeleteGlobalRef(jniEnv, self->delegateObject);
|
||||
self->delegateObject = NULL;
|
||||
self->vm = NULL;
|
||||
}
|
||||
|
||||
if(delegate)
|
||||
{
|
||||
delegate = (*jniEnv)->NewGlobalRef(jniEnv, delegate);
|
||||
if(delegate)
|
||||
{
|
||||
(*jniEnv)->GetJavaVM(jniEnv, &(self->vm));
|
||||
self->delegateObject = delegate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void) hotkeyAction:(NSEvent*)hotKeyEvent inObject:(id)anObject;
|
||||
{
|
||||
int modifiers = [hotKeyEvent modifierFlags];
|
||||
int keycode = [hotKeyEvent keyCode];
|
||||
int javaModifiers = 0;
|
||||
int javaKeycode = 0;
|
||||
|
||||
(void)anObject;
|
||||
|
||||
javaKeycode = convertMacKeycodeToJava(keycode);
|
||||
javaModifiers = convertMacModifiersToJavaUserDefined(modifiers);
|
||||
|
||||
[self notify:javaKeycode inModifiers:javaModifiers];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if((self = [super init]))
|
||||
{
|
||||
self->delegateObject = NULL;
|
||||
self->vm = NULL;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self setDelegate:NULL inJNIEnv:NULL];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (int) registerKey:(int)keycode inModifiers:(int)modifiers
|
||||
{
|
||||
NSAutoreleasePool* autoreleasePool = NULL;
|
||||
DDHotKeyCenter* c = NULL;
|
||||
|
||||
autoreleasePool = [[NSAutoreleasePool alloc] init];
|
||||
c = [[DDHotKeyCenter alloc] init];
|
||||
|
||||
/*
|
||||
DDHotKeyTask task = ^(NSEvent* hkEvent)
|
||||
{
|
||||
printf("hot task\n");fflush(stdout);
|
||||
};
|
||||
*/
|
||||
|
||||
if(![c registerHotKeyWithKeyCode:keycode modifierFlags:(modifiers) target:self action:@selector(hotkeyAction:inObject:) object:nil])
|
||||
{
|
||||
[c release];
|
||||
[autoreleasePool release];
|
||||
return 0;
|
||||
}
|
||||
[c release];
|
||||
[autoreleasePool release];
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (void) unregisterKey:(int)keycode inModifiers:(int)modifiers
|
||||
{
|
||||
NSAutoreleasePool* autoreleasePool;
|
||||
DDHotKeyCenter* c = NULL;
|
||||
|
||||
autoreleasePool = [[NSAutoreleasePool alloc] init];
|
||||
c = [[DDHotKeyCenter alloc] init];
|
||||
|
||||
[c unregisterHotKeyWithKeyCode:keycode modifierFlags:(modifiers)];
|
||||
[c release];
|
||||
[autoreleasePool release];
|
||||
}
|
||||
@end
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_init
|
||||
(JNIEnv* jniEnv, jclass clazz)
|
||||
{
|
||||
KeyboardHook* keyboard = NULL;
|
||||
NSAutoreleasePool* autoreleasePool;
|
||||
|
||||
(void)jniEnv;
|
||||
(void)clazz;
|
||||
|
||||
autoreleasePool = [[NSAutoreleasePool alloc] init];
|
||||
keyboard = [[KeyboardHook alloc] init];
|
||||
|
||||
[autoreleasePool release];
|
||||
|
||||
return (jlong)keyboard;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_start
|
||||
(JNIEnv* jniEnv, jclass clazz, jlong ptr)
|
||||
{
|
||||
(void)jniEnv;
|
||||
(void)clazz;
|
||||
(void)ptr;
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_stop
|
||||
(JNIEnv* jniEnv, jclass clazz, jlong ptr)
|
||||
{
|
||||
(void)jniEnv;
|
||||
(void)clazz;
|
||||
(void)ptr;
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_setDelegate
|
||||
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jobject delegate)
|
||||
{
|
||||
KeyboardHook* keyboard = NULL;
|
||||
|
||||
(void)clazz;
|
||||
|
||||
if(delegate)
|
||||
{
|
||||
keyboard = (KeyboardHook*)ptr;
|
||||
[keyboard setDelegate:delegate inJNIEnv:jniEnv];
|
||||
}
|
||||
else
|
||||
{
|
||||
keyboard = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_registerShortcut
|
||||
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode, jint modifiers)
|
||||
{
|
||||
KeyboardHook* keyboard = (KeyboardHook*)ptr;
|
||||
|
||||
(void)jniEnv;
|
||||
(void)clazz;
|
||||
|
||||
if(keyboard)
|
||||
{
|
||||
int macKeycode = convertJavaKeycodeToMac(keycode);
|
||||
int macModifiers = convertJavaUserDefinedModifiersToMac(modifiers);
|
||||
if(macKeycode == -1)
|
||||
{
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
if([keyboard registerKey:macKeycode inModifiers:macModifiers])
|
||||
{
|
||||
return JNI_TRUE;
|
||||
}
|
||||
}
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterShortcut
|
||||
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode, jint modifiers)
|
||||
{
|
||||
KeyboardHook* keyboard = (KeyboardHook*)ptr;
|
||||
|
||||
(void)jniEnv;
|
||||
(void)clazz;
|
||||
|
||||
if(keyboard)
|
||||
{
|
||||
int macKeycode = convertJavaKeycodeToMac(keycode);
|
||||
int macModifiers = convertJavaUserDefinedModifiersToMac(modifiers);
|
||||
|
||||
if(macModifiers == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[keyboard unregisterKey:macKeycode inModifiers:macModifiers];
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.globalshortcut;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List; // disambiguation
|
||||
|
||||
import net.java.sip.communicator.service.globalshortcut.*;
|
||||
import net.java.sip.communicator.service.keybindings.*;
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.service.protocol.event.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Shortcut for call (take the call, hang up, ...).
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public class CallShortcut
|
||||
implements GlobalShortcutListener,
|
||||
CallListener
|
||||
{
|
||||
/**
|
||||
* The <tt>Logger</tt> used by the <tt>CallShortcut</tt> class
|
||||
* and its instances for logging output.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(CallShortcut.class);
|
||||
|
||||
/**
|
||||
* Keybindings service.
|
||||
*/
|
||||
private KeybindingsService keybindingsService =
|
||||
GlobalShortcutActivator.getKeybindingsService();
|
||||
|
||||
/**
|
||||
* List of incoming calls.
|
||||
*/
|
||||
private ArrayList<Call> incomingCalls = new ArrayList<Call>();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CallShortcut()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when an shortcut is typed
|
||||
*
|
||||
* @param evt <tt>GlobalShortcutEvent</tt>
|
||||
*/
|
||||
public void shortcutReceived(GlobalShortcutEvent evt)
|
||||
{
|
||||
AWTKeyStroke keystroke = evt.getKeyStroke();
|
||||
GlobalKeybindingSet set = keybindingsService.getGlobalBindings();
|
||||
|
||||
Call choosenCall = null;
|
||||
|
||||
for(Map.Entry<String, List<AWTKeyStroke>> entry :
|
||||
set.getBindings().entrySet())
|
||||
{
|
||||
for(AWTKeyStroke ks : entry.getValue())
|
||||
{
|
||||
if(entry.getKey().equals("answer") &&
|
||||
keystroke.getKeyCode() == ks.getKeyCode() &&
|
||||
keystroke.getModifiers() == ks.getModifiers())
|
||||
{
|
||||
synchronized(incomingCalls)
|
||||
{
|
||||
int size = incomingCalls.size();
|
||||
|
||||
for(int i = 0 ; i < size ; i++)
|
||||
{
|
||||
Call c = incomingCalls.get(i);
|
||||
|
||||
if(c.getCallPeers().next().getState() ==
|
||||
CallPeerState.INCOMING_CALL)
|
||||
{
|
||||
choosenCall = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(choosenCall == null)
|
||||
return;
|
||||
|
||||
final OperationSetBasicTelephony<?> opSet =
|
||||
choosenCall.getProtocolProvider().getOperationSet(
|
||||
OperationSetBasicTelephony.class);
|
||||
final Call cCall = choosenCall;
|
||||
|
||||
new Thread()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
opSet.answerCallPeer(cCall.getCallPeers().next());
|
||||
}
|
||||
catch(OperationFailedException e)
|
||||
{
|
||||
logger.info("Failed to answer call via global shortcut", e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
else if(entry.getKey().equals("hangup") &&
|
||||
keystroke.getKeyCode() == ks.getKeyCode() &&
|
||||
keystroke.getModifiers() == ks.getModifiers())
|
||||
{
|
||||
Call incomingCall = null;
|
||||
|
||||
synchronized(incomingCalls)
|
||||
{
|
||||
int size = incomingCalls.size();
|
||||
|
||||
for(int i = 0 ; i < size ; i++)
|
||||
{
|
||||
Call c = incomingCalls.get(i);
|
||||
|
||||
if(c.getCallPeers().next().getState() ==
|
||||
CallPeerState.CONNECTED)
|
||||
{
|
||||
choosenCall = c;
|
||||
break;
|
||||
}
|
||||
else if(c.getCallPeers().next().getState() ==
|
||||
CallPeerState.INCOMING_CALL && incomingCall == null)
|
||||
{
|
||||
incomingCall = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(choosenCall == null && incomingCall != null)
|
||||
{
|
||||
// maybe we just want to hangup (refuse) incoming call
|
||||
choosenCall = incomingCall;
|
||||
}
|
||||
|
||||
if(choosenCall == null)
|
||||
return;
|
||||
|
||||
final OperationSetBasicTelephony<?> opSet =
|
||||
choosenCall.getProtocolProvider().getOperationSet(
|
||||
OperationSetBasicTelephony.class);
|
||||
|
||||
final Call cCall = choosenCall;
|
||||
new Thread()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
opSet.hangupCallPeer(cCall.getCallPeers().next());
|
||||
}
|
||||
catch(OperationFailedException e)
|
||||
{
|
||||
logger.info("Failed to answer call via global shortcut", e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void incomingCallReceived(CallEvent event)
|
||||
{
|
||||
Call sourceCall = event.getSourceCall();
|
||||
|
||||
synchronized(incomingCalls)
|
||||
{
|
||||
incomingCalls.add(sourceCall);
|
||||
}
|
||||
}
|
||||
|
||||
public void outgoingCallCreated(CallEvent event)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
public void callEnded(CallEvent event)
|
||||
{
|
||||
Call sourceCall = event.getSourceCall();
|
||||
incomingCalls.remove(sourceCall);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.globalshortcut;
|
||||
|
||||
import net.java.sip.communicator.service.globalshortcut.*;
|
||||
import net.java.sip.communicator.service.gui.*;
|
||||
import net.java.sip.communicator.service.keybindings.*;
|
||||
import net.java.sip.communicator.service.protocol.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
import org.osgi.framework.*;
|
||||
|
||||
/**
|
||||
* OSGi Activator for global shortcut.
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public class GlobalShortcutActivator
|
||||
implements BundleActivator
|
||||
{
|
||||
/**
|
||||
* The <tt>Logger</tt> used by the <tt>GlobalShortcutActivator</tt> class
|
||||
* and its instances for logging output.
|
||||
*/
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(GlobalShortcutActivator.class);
|
||||
|
||||
/**
|
||||
* The OSGi <tt>ServiceRegistration</tt> of <tt>GlobalShortcut</tt>.
|
||||
*/
|
||||
private ServiceRegistration serviceRegistration;
|
||||
|
||||
/**
|
||||
* The <tt>GlobalShortcutServiceImpl</tt>.
|
||||
*/
|
||||
protected static GlobalShortcutServiceImpl globalShortcutService = null;
|
||||
|
||||
/**
|
||||
* OSGi bundle context.
|
||||
*/
|
||||
private static BundleContext bundleContext = null;
|
||||
|
||||
/**
|
||||
* Keybindings service reference.
|
||||
*/
|
||||
private static KeybindingsService keybindingsService = null;
|
||||
|
||||
/**
|
||||
* UI service reference.
|
||||
*/
|
||||
private static UIService uiService = null;
|
||||
|
||||
/**
|
||||
* Returns the <tt>KeybindingsService</tt> obtained from the bundle context.
|
||||
*
|
||||
* @return the <tt>KeybindingsService</tt> obtained from the bundle context
|
||||
*/
|
||||
public static KeybindingsService getKeybindingsService()
|
||||
{
|
||||
if (keybindingsService == null)
|
||||
{
|
||||
keybindingsService
|
||||
= ServiceUtils.getService(
|
||||
bundleContext,
|
||||
KeybindingsService.class);
|
||||
}
|
||||
return keybindingsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <tt>UIService</tt> obtained from the bundle context.
|
||||
*
|
||||
* @return the <tt>UIService</tt> obtained from the bundle context
|
||||
*/
|
||||
public static UIService getUIService()
|
||||
{
|
||||
if (uiService == null)
|
||||
{
|
||||
uiService
|
||||
= ServiceUtils.getService(
|
||||
bundleContext,
|
||||
UIService.class);
|
||||
}
|
||||
return uiService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the execution of this service bundle in the specified context.
|
||||
*
|
||||
* @param bundleContext the context in which the service bundle is to
|
||||
* start executing
|
||||
* @throws Exception if an error occurs while starting the execution of the
|
||||
* service bundle in the specified context
|
||||
*/
|
||||
public void start(BundleContext bundleContext)
|
||||
throws Exception
|
||||
{
|
||||
GlobalShortcutActivator.bundleContext = bundleContext;
|
||||
serviceRegistration = null;
|
||||
globalShortcutService = new GlobalShortcutServiceImpl();
|
||||
globalShortcutService.start();
|
||||
bundleContext.registerService(GlobalShortcutService.class.getName(),
|
||||
globalShortcutService, null);
|
||||
|
||||
globalShortcutService.reloadGlobalShortcuts();
|
||||
|
||||
registerListenerWithProtocolProviderService();
|
||||
|
||||
bundleContext.addServiceListener(new ServiceListener()
|
||||
{
|
||||
public void serviceChanged(ServiceEvent serviceEvent)
|
||||
{
|
||||
GlobalShortcutActivator.this.serviceChanged(serviceEvent);
|
||||
}
|
||||
});
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("GlobalShortcut Service ... [REGISTERED]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the execution of this service bundle in the specified context.
|
||||
*
|
||||
* @param bundleContext the context in which this service bundle is to
|
||||
* stop executing
|
||||
* @throws Exception if an error occurs while stopping the execution of the
|
||||
* service bundle in the specified context
|
||||
*/
|
||||
public void stop(BundleContext bundleContext)
|
||||
throws Exception
|
||||
{
|
||||
if (serviceRegistration != null)
|
||||
{
|
||||
globalShortcutService.stop();
|
||||
serviceRegistration.unregister();
|
||||
serviceRegistration = null;
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("GlobalShortcut Service ... [UNREGISTERED]");
|
||||
}
|
||||
|
||||
bundleContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the <tt>ServiceListener</tt> method. Verifies whether the
|
||||
* passed event concerns a <tt>ProtocolProviderService</tt> and adds the
|
||||
* corresponding UI controls.
|
||||
*
|
||||
* @param event The <tt>ServiceEvent</tt> object.
|
||||
*/
|
||||
private void serviceChanged(ServiceEvent event)
|
||||
{
|
||||
ServiceReference serviceRef = event.getServiceReference();
|
||||
|
||||
// if the event is caused by a bundle being stopped, we don't want to
|
||||
// know
|
||||
if (serviceRef.getBundle().getState() == Bundle.STOPPING)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Object service = bundleContext.getService(serviceRef);
|
||||
|
||||
// we don't care if the source service is not a protocol provider
|
||||
if (!(service instanceof ProtocolProviderService))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.getType())
|
||||
{
|
||||
case ServiceEvent.REGISTERED:
|
||||
this.handleProviderAdded((ProtocolProviderService) service);
|
||||
break;
|
||||
case ServiceEvent.UNREGISTERING:
|
||||
this.handleProviderRemoved((ProtocolProviderService) service);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered <tt>ProtocolProviderService</tt> and set our listener.
|
||||
*/
|
||||
public void registerListenerWithProtocolProviderService()
|
||||
{
|
||||
ServiceReference refs[] = null;
|
||||
try
|
||||
{
|
||||
refs = bundleContext.getServiceReferences(
|
||||
ProtocolProviderService.class.getName(), null);
|
||||
}
|
||||
catch (InvalidSyntaxException e)
|
||||
{
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for(ServiceReference ref : refs)
|
||||
{
|
||||
ProtocolProviderService service =
|
||||
(ProtocolProviderService)bundleContext.getService(ref);
|
||||
|
||||
OperationSetBasicTelephony<?> opSet =
|
||||
service.getOperationSet(OperationSetBasicTelephony.class);
|
||||
|
||||
opSet.addCallListener(globalShortcutService.getCallShortcut());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this manager that a specific
|
||||
* <tt>ProtocolProviderService</tt> has been registered as a service.
|
||||
*
|
||||
* @param provider the <tt>ProtocolProviderService</tt> which has been
|
||||
* registered as a service.
|
||||
*/
|
||||
private void handleProviderAdded(final ProtocolProviderService provider)
|
||||
{
|
||||
OperationSetBasicTelephony<?> opSet =
|
||||
provider.getOperationSet(OperationSetBasicTelephony.class);
|
||||
opSet.addCallListener(globalShortcutService.getCallShortcut());
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this manager that a specific
|
||||
* <tt>ProtocolProviderService</tt> has been unregistered as a service.
|
||||
*
|
||||
* @param provider the <tt>ProtocolProviderService</tt> which has been
|
||||
* unregistered as a service.
|
||||
*/
|
||||
private void handleProviderRemoved(ProtocolProviderService provider)
|
||||
{
|
||||
OperationSetBasicTelephony<?> opSet =
|
||||
provider.getOperationSet(OperationSetBasicTelephony.class);
|
||||
opSet.removeCallListener(globalShortcutService.getCallShortcut());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.globalshortcut;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.List; // disambiguation
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import net.java.sip.communicator.service.globalshortcut.*;
|
||||
import net.java.sip.communicator.service.keybindings.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* This global shortcut service permits to register listeners for global
|
||||
* shortcut (i.e. keystroke even if application is not foreground).
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public class GlobalShortcutServiceImpl
|
||||
implements GlobalShortcutService,
|
||||
NativeKeyboardHookDelegate
|
||||
{
|
||||
/**
|
||||
* The <tt>Logger</tt> used by the <tt>GlobalShortcutServiceImpl</tt> class
|
||||
* and its instances for logging output.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(
|
||||
GlobalShortcutServiceImpl.class);
|
||||
|
||||
/**
|
||||
* List of action and its corresponding shortcut.
|
||||
*/
|
||||
private final Map<GlobalShortcutListener, List<AWTKeyStroke>> mapActions =
|
||||
new HashMap<GlobalShortcutListener, List<AWTKeyStroke>>();
|
||||
|
||||
/**
|
||||
* If the service is running or not.
|
||||
*/
|
||||
private boolean isRunning = false;
|
||||
|
||||
/**
|
||||
* The <tt>NativeKeyboardHook</tt> that will notify us key press event.
|
||||
*/
|
||||
private NativeKeyboardHook keyboardHook = new NativeKeyboardHook();
|
||||
|
||||
/**
|
||||
* Call shortcut to answer/hang up a call.
|
||||
*/
|
||||
private final CallShortcut callShortcut = new CallShortcut();
|
||||
|
||||
/**
|
||||
* UI shortcut to display GUI.
|
||||
*/
|
||||
private final UIShortcut uiShortcut = new UIShortcut();
|
||||
|
||||
/**
|
||||
* Initializes the <tt>GlobalShortcutServiceImpl</tt>.
|
||||
*/
|
||||
public GlobalShortcutServiceImpl()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an action to execute when the keystroke is typed.
|
||||
*
|
||||
* @param listener listener to notify when keystroke is typed
|
||||
* @param keyStroke keystroke that will trigger the action
|
||||
*/
|
||||
public void registerShortcut(GlobalShortcutListener listener,
|
||||
AWTKeyStroke keyStroke)
|
||||
{
|
||||
List<AWTKeyStroke> keystrokes = mapActions.get(listener);
|
||||
|
||||
if(keyStroke == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(keystrokes != null)
|
||||
{
|
||||
if(keyboardHook.registerShortcut(keyStroke.getKeyCode(),
|
||||
getModifiers(keyStroke)))
|
||||
{
|
||||
keystrokes.add(keyStroke);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
keystrokes = new ArrayList<AWTKeyStroke>();
|
||||
if(keyboardHook.registerShortcut(keyStroke.getKeyCode(),
|
||||
getModifiers(keyStroke)))
|
||||
{
|
||||
keystrokes.add(keyStroke);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized(mapActions)
|
||||
{
|
||||
mapActions.put(listener, keystrokes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters an action to execute when the keystroke is typed.
|
||||
*
|
||||
* @param listener listener to remove
|
||||
* @param keyStroke keystroke that will trigger the action
|
||||
*/
|
||||
public void unregisterShortcut(GlobalShortcutListener listener,
|
||||
AWTKeyStroke keyStroke)
|
||||
{
|
||||
List<AWTKeyStroke> keystrokes = mapActions.get(listener);
|
||||
|
||||
if(keystrokes != null && keyStroke != null)
|
||||
{
|
||||
int keycode = keyStroke.getKeyCode();
|
||||
int modifiers = keyStroke.getModifiers();
|
||||
AWTKeyStroke ks = null;
|
||||
|
||||
for(AWTKeyStroke l : keystrokes)
|
||||
{
|
||||
if(l.getKeyCode() == keycode && l.getModifiers() == modifiers)
|
||||
ks = l;
|
||||
}
|
||||
|
||||
if(ks != null)
|
||||
{
|
||||
keystrokes.remove(ks);
|
||||
}
|
||||
|
||||
keyboardHook.unregisterShortcut(keyStroke.getKeyCode(),
|
||||
getModifiers(keyStroke));
|
||||
|
||||
synchronized(mapActions)
|
||||
{
|
||||
if(keystrokes.size() == 0)
|
||||
{
|
||||
mapActions.remove(listener);
|
||||
}
|
||||
else
|
||||
{
|
||||
mapActions.put(listener, keystrokes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the service.
|
||||
*/
|
||||
public void start()
|
||||
{
|
||||
if(!isRunning)
|
||||
{
|
||||
keyboardHook.setDelegate(this);
|
||||
keyboardHook.start();
|
||||
isRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the service.
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
Collection<List<AWTKeyStroke>> lst = mapActions.values();
|
||||
|
||||
isRunning = false;
|
||||
|
||||
for(List<AWTKeyStroke> kss : lst)
|
||||
{
|
||||
for(AWTKeyStroke e : kss)
|
||||
{
|
||||
unregisterShortcut(callShortcut, e);
|
||||
unregisterShortcut(uiShortcut, e);
|
||||
}
|
||||
}
|
||||
|
||||
if(keyboardHook != null)
|
||||
{
|
||||
keyboardHook.setDelegate(null);
|
||||
keyboardHook.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a key press event.
|
||||
*
|
||||
* @param keycode keycode received
|
||||
* @param modifiers modifiers received (ALT or CTRL + letter, ...)
|
||||
*/
|
||||
public void receiveKey(int keycode, int modifiers)
|
||||
{
|
||||
synchronized(mapActions)
|
||||
{
|
||||
// compare keycode/modifiers to keystroke
|
||||
for(Map.Entry<GlobalShortcutListener, List<AWTKeyStroke>> entry :
|
||||
mapActions.entrySet())
|
||||
{
|
||||
List<AWTKeyStroke> lst = entry.getValue();
|
||||
|
||||
for(AWTKeyStroke l : lst)
|
||||
{
|
||||
if(l.getKeyCode() == keycode &&
|
||||
getModifiers(l) == modifiers)
|
||||
{
|
||||
// notify corresponding listeners
|
||||
GlobalShortcutEvent evt = new GlobalShortcutEvent(l);
|
||||
entry.getKey().shortcutReceived(evt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our user-defined modifiers.
|
||||
*
|
||||
* @param keystroke keystroke
|
||||
* @return user-defined modifiers
|
||||
*/
|
||||
private static int getModifiers(AWTKeyStroke keystroke)
|
||||
{
|
||||
int modifiers = keystroke.getModifiers();
|
||||
int ret = 0;
|
||||
|
||||
if((modifiers & java.awt.event.InputEvent.CTRL_DOWN_MASK) > 0)
|
||||
{
|
||||
ret |= NativeKeyboardHookDelegate.MODIFIERS_CTRL;
|
||||
}
|
||||
if((modifiers & java.awt.event.InputEvent.ALT_DOWN_MASK) > 0)
|
||||
{
|
||||
ret |= NativeKeyboardHookDelegate.MODIFIERS_ALT;
|
||||
}
|
||||
if((modifiers & java.awt.event.InputEvent.SHIFT_DOWN_MASK) > 0)
|
||||
{
|
||||
ret |= NativeKeyboardHookDelegate.MODIFIERS_SHIFT;
|
||||
}
|
||||
if((modifiers & java.awt.event.InputEvent.META_DOWN_MASK) > 0)
|
||||
{
|
||||
ret |= NativeKeyboardHookDelegate.MODIFIERS_LOGO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reload global shortcuts.
|
||||
*/
|
||||
public synchronized void reloadGlobalShortcuts()
|
||||
{
|
||||
// unregister all shortcuts
|
||||
GlobalKeybindingSet set =
|
||||
GlobalShortcutActivator.getKeybindingsService().getGlobalBindings();
|
||||
|
||||
for(Map.Entry<String, List<AWTKeyStroke>> entry :
|
||||
set.getBindings().entrySet())
|
||||
{
|
||||
for(AWTKeyStroke e : entry.getValue())
|
||||
{
|
||||
unregisterShortcut(callShortcut, e);
|
||||
unregisterShortcut(uiShortcut, e);
|
||||
}
|
||||
}
|
||||
|
||||
// add shortcuts from configuration
|
||||
for(Map.Entry<String, List<AWTKeyStroke>> entry :
|
||||
set.getBindings().entrySet())
|
||||
{
|
||||
if(entry.getKey().equals("answer") ||
|
||||
entry.getKey().equals("hangup"))
|
||||
{
|
||||
for(AWTKeyStroke e : entry.getValue())
|
||||
{
|
||||
registerShortcut(callShortcut, e);
|
||||
}
|
||||
}
|
||||
else if(entry.getKey().equals("contactlist"))
|
||||
{
|
||||
for(AWTKeyStroke e : entry.getValue())
|
||||
{
|
||||
registerShortcut(uiShortcut, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns CallShortcut object.
|
||||
*
|
||||
* @return CallShortcut object
|
||||
*/
|
||||
public CallShortcut getCallShortcut()
|
||||
{
|
||||
return callShortcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns UIShortcut object.
|
||||
*
|
||||
* @return UIShortcut object
|
||||
*/
|
||||
public UIShortcut getUIShortcut()
|
||||
{
|
||||
return uiShortcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple test.
|
||||
*/
|
||||
public void test()
|
||||
{
|
||||
GlobalShortcutListener l = new GlobalShortcutListener()
|
||||
{
|
||||
public void shortcutReceived(GlobalShortcutEvent evt)
|
||||
{
|
||||
System.out.println("global shortcut event");
|
||||
}
|
||||
};
|
||||
|
||||
AWTKeyStroke ks = AWTKeyStroke.getAWTKeyStroke("control B");
|
||||
AWTKeyStroke ks2 = AWTKeyStroke.getAWTKeyStroke("control E");
|
||||
|
||||
if(ks == null)
|
||||
{
|
||||
logger.info("Failed to register keystroke");
|
||||
System.out.println("failed to register keystroke");
|
||||
return;
|
||||
}
|
||||
|
||||
this.registerShortcut(l, ks);
|
||||
this.registerShortcut(l, ks2);
|
||||
try{Thread.sleep(30000);}catch(InterruptedException e){}
|
||||
this.unregisterShortcut(l, ks);
|
||||
try{Thread.sleep(5000);}catch(InterruptedException e){}
|
||||
this.unregisterShortcut(l, ks2);
|
||||
|
||||
/*
|
||||
boolean ret = keyboardHook.registerShortcut(ks.getKeyCode(),
|
||||
getModifiers(ks));
|
||||
System.out.println("finally " + ret);
|
||||
|
||||
System.out.println("registered");
|
||||
try{Thread.sleep(30000);}catch(InterruptedException e){}
|
||||
System.out.println("unregistered1");
|
||||
keyboardHook.unregisterShortcut(ks.getKeyCode(),
|
||||
getModifiers(ks));
|
||||
System.out.println("unregistered2");
|
||||
*/
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.globalshortcut;
|
||||
|
||||
import net.java.sip.communicator.util.*;
|
||||
|
||||
/**
|
||||
* Native hook for keyboard. It is used to notify a
|
||||
* <tt>NativeKeyboardHookDelegate</tt> for key (even if key are pressed when
|
||||
* application is not in foreground).
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public class NativeKeyboardHook
|
||||
{
|
||||
/**
|
||||
* The <tt>Logger</tt> used by the <tt>NativeKeyboardHook</tt> class and its
|
||||
* instances for logging output.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(
|
||||
NativeKeyboardHook.class);
|
||||
|
||||
/**
|
||||
* If it is started.
|
||||
*/
|
||||
private boolean isStarted = false;
|
||||
|
||||
/**
|
||||
* Native pointer.
|
||||
*/
|
||||
private static long ptr = 0;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public NativeKeyboardHook()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the <tt>NativeKeyboardHook</tt>.
|
||||
*/
|
||||
public synchronized void start()
|
||||
{
|
||||
if(!isStarted && ptr != 0)
|
||||
{
|
||||
isStarted = true;
|
||||
start(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the <tt>NativeKeyboardHook</tt>.
|
||||
*/
|
||||
public synchronized void stop()
|
||||
{
|
||||
if(isStarted && ptr != 0)
|
||||
{
|
||||
isStarted = false;
|
||||
stop(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set delegate object for event notification.
|
||||
*
|
||||
* @param delegate delegate object
|
||||
*/
|
||||
public synchronized void setDelegate(NativeKeyboardHookDelegate delegate)
|
||||
{
|
||||
if(ptr != 0)
|
||||
setDelegate(ptr, delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a shortcut.
|
||||
*
|
||||
* @param keycode keycode of the shortcut
|
||||
* @param modifiers modifiers (CTRL, ALT, ...)
|
||||
* @return true if success, false otherwise
|
||||
*/
|
||||
public synchronized boolean registerShortcut(int keycode,
|
||||
int modifiers)
|
||||
{
|
||||
if(ptr != 0)
|
||||
return registerShortcut(ptr, keycode, modifiers);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a shortcut.
|
||||
*
|
||||
* @param keycode keycode of the shortcut
|
||||
* @param modifiers modifiers (CTRL, ALT, ...)
|
||||
*/
|
||||
public synchronized void unregisterShortcut(int keycode,
|
||||
int modifiers)
|
||||
{
|
||||
if(ptr != 0)
|
||||
unregisterShortcut(ptr, keycode, modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Native method to initialize <tt>NativeKeyboardHook</tt>.
|
||||
*
|
||||
* @return native pointer.
|
||||
*/
|
||||
private static native long init();
|
||||
|
||||
/**
|
||||
* Native method to start <tt>NativeKeyboardHook</tt>.
|
||||
*
|
||||
* @param ptr native pointer
|
||||
*/
|
||||
private static native void start(long ptr);
|
||||
|
||||
/**
|
||||
* Native method to stop <tt>NativeKeyboardHook</tt>.
|
||||
*
|
||||
* @param ptr native pointer
|
||||
*/
|
||||
private static native void stop(long ptr);
|
||||
|
||||
/**
|
||||
* Native method to set the delegate object.
|
||||
*
|
||||
* @param ptr native pointer
|
||||
* @param delegate delegate object to set
|
||||
*/
|
||||
private static native void setDelegate(long ptr,
|
||||
NativeKeyboardHookDelegate delegate);
|
||||
|
||||
/**
|
||||
* Native method to register a shortcut.
|
||||
*
|
||||
* @param ptr native pointer
|
||||
* @param keycode keycode of the shortcut
|
||||
* @param modifiers modifiers (CTRL, ALT, ...)
|
||||
*/
|
||||
private static native boolean registerShortcut(long ptr, int keycode,
|
||||
int modifiers);
|
||||
|
||||
/**
|
||||
* Native method to unregister a shortcut.
|
||||
*
|
||||
* @param ptr native pointer
|
||||
* @param keycode keycode of the shortcut
|
||||
* @param modifiers modifiers (CTRL, ALT, ...)
|
||||
*/
|
||||
private static native void unregisterShortcut(long ptr, int keycode,
|
||||
int modifiers);
|
||||
|
||||
static
|
||||
{
|
||||
try
|
||||
{
|
||||
System.loadLibrary("globalshortcut");
|
||||
ptr = init();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
logger.warn("Failed to load globalshortcut", e);
|
||||
ptr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.globalshortcut;
|
||||
|
||||
/**
|
||||
* NativeKeyboardHookDelegate interface.
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public interface NativeKeyboardHookDelegate
|
||||
{
|
||||
/**
|
||||
* CTRL modifier.
|
||||
*/
|
||||
public static final int MODIFIERS_CTRL = 1;
|
||||
|
||||
/**
|
||||
* ALT modifier.
|
||||
*/
|
||||
public static final int MODIFIERS_ALT = 2;
|
||||
|
||||
/**
|
||||
* SHIFT modifier.
|
||||
*/
|
||||
public static final int MODIFIERS_SHIFT = 4;
|
||||
|
||||
/**
|
||||
* Logo modifier (i.e. CMD/Apple key on Mac OS X, Windows key on
|
||||
* MS Windows).
|
||||
*/
|
||||
public static final int MODIFIERS_LOGO = 8;
|
||||
|
||||
/**
|
||||
* Receive a key press event.
|
||||
*
|
||||
* @param keycode keycode received
|
||||
* @param modifiers modifiers received (ALT or CTRL + letter, ...)
|
||||
*/
|
||||
public void receiveKey(int keycode, int modifiers);
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.globalshortcut;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List; // disambiguation
|
||||
|
||||
import net.java.sip.communicator.service.globalshortcut.*;
|
||||
import net.java.sip.communicator.service.gui.*;
|
||||
import net.java.sip.communicator.service.keybindings.*;
|
||||
|
||||
/**
|
||||
* UI shortcut.
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public class UIShortcut
|
||||
implements GlobalShortcutListener
|
||||
{
|
||||
/**
|
||||
* Keybindings service.
|
||||
*/
|
||||
private KeybindingsService keybindingsService =
|
||||
GlobalShortcutActivator.getKeybindingsService();
|
||||
|
||||
/**
|
||||
* Callback when an shortcut is typed
|
||||
*
|
||||
* @param evt <tt>GlobalShortcutEvent</tt>
|
||||
*/
|
||||
public void shortcutReceived(GlobalShortcutEvent evt)
|
||||
{
|
||||
AWTKeyStroke keystroke = evt.getKeyStroke();
|
||||
GlobalKeybindingSet set = keybindingsService.getGlobalBindings();
|
||||
|
||||
for(Map.Entry<String, List<AWTKeyStroke>> entry :
|
||||
set.getBindings().entrySet())
|
||||
{
|
||||
for(AWTKeyStroke ks : entry.getValue())
|
||||
{
|
||||
if(entry.getKey().equals("contactlist") &&
|
||||
keystroke.getKeyCode() == ks.getKeyCode() &&
|
||||
keystroke.getModifiers() == ks.getModifiers())
|
||||
{
|
||||
ExportedWindow window =
|
||||
GlobalShortcutActivator.getUIService().
|
||||
getExportedWindow(ExportedWindow.MAIN_WINDOW);
|
||||
|
||||
if(window == null)
|
||||
return;
|
||||
|
||||
window.bringToFront();
|
||||
window.setVisible(true);
|
||||
if(window instanceof Window)
|
||||
{
|
||||
((Window)window).setAlwaysOnTop(true);
|
||||
((Window)window).setAlwaysOnTop(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
Bundle-Activator: net.java.sip.communicator.impl.globalshortcut.GlobalShortcutActivator
|
||||
Bundle-Name: Global shortcut
|
||||
Bundle-Description: A bundle which implements global shortcut
|
||||
Bundle-Vendor: sip-communicator.org
|
||||
Bundle-Version: 0.0.1
|
||||
System-Bundle: yes
|
||||
Import-Package: org.osgi.framework,
|
||||
net.java.sip.communicator.service.protocol,
|
||||
net.java.sip.communicator.service.protocol.event,
|
||||
net.java.sip.communicator.service.keybindings,
|
||||
net.java.sip.communicator.service.gui,
|
||||
net.java.sip.communicator.util,
|
||||
Export-package: net.java.sip.communicator.service.globalshortcut,
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.impl.keybindings;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import net.java.sip.communicator.service.keybindings.*;
|
||||
|
||||
/**
|
||||
* Global keybinding set.
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public class GlobalKeybindingSetImpl
|
||||
implements GlobalKeybindingSet
|
||||
{
|
||||
/**
|
||||
* List of bindings (name and list of different keystroke that would
|
||||
* trigger the action).
|
||||
*/
|
||||
private Map<String, List<AWTKeyStroke>> bindings = new
|
||||
LinkedHashMap<String, List<AWTKeyStroke>>();
|
||||
|
||||
/**
|
||||
* Provides current keybinding mappings.
|
||||
* @return mapping of keystrokes to the string representation of the actions
|
||||
* they perform
|
||||
*/
|
||||
public Map<String, List<AWTKeyStroke>> getBindings()
|
||||
{
|
||||
return new LinkedHashMap<String, List<AWTKeyStroke>>(this.bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the bindings and notifies the observer's listeners if they've
|
||||
* changed.
|
||||
* @param bindings new keybindings to be held
|
||||
*/
|
||||
public void setBindings(Map<String, List<AWTKeyStroke>> bindings)
|
||||
{
|
||||
if (!this.bindings.equals(bindings))
|
||||
{
|
||||
this.bindings.clear();
|
||||
this.bindings.putAll(bindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.plugin.keybindingchooser.globalchooser;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
import net.java.sip.communicator.plugin.keybindingchooser.*;
|
||||
import net.java.sip.communicator.service.globalshortcut.*;
|
||||
import net.java.sip.communicator.service.keybindings.*;
|
||||
import net.java.sip.communicator.util.*;
|
||||
import net.java.sip.communicator.util.swing.*;
|
||||
|
||||
/**
|
||||
* This ConfigurationForm shows the list of global shortcut
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public class GlobalShortcutConfigForm
|
||||
extends TransparentPanel
|
||||
implements ListSelectionListener
|
||||
{
|
||||
/**
|
||||
* Serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* The logger for this class.
|
||||
*/
|
||||
private static Logger logger = Logger.getLogger(
|
||||
GlobalShortcutConfigForm.class);
|
||||
|
||||
/**
|
||||
* Displays the registered shortcuts.
|
||||
*/
|
||||
private JTable shortcutsTable = new JTable();
|
||||
|
||||
/**
|
||||
* Contains the shortcutsTable.
|
||||
*/
|
||||
private JScrollPane scrollPane = new JScrollPane();
|
||||
|
||||
/**
|
||||
* Contains listPanel.
|
||||
*/
|
||||
private JPanel mainPanel = this;
|
||||
|
||||
/**
|
||||
* Model for the shortcutsTable
|
||||
*/
|
||||
private GlobalShortcutTableModel tableModel =
|
||||
new GlobalShortcutTableModel();
|
||||
|
||||
/**
|
||||
* Current selected row.
|
||||
*/
|
||||
private int currentRow = -1;
|
||||
|
||||
/**
|
||||
* Current selected row.
|
||||
*/
|
||||
private int currentColumn = -1;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public GlobalShortcutConfigForm()
|
||||
{
|
||||
super(new BorderLayout());
|
||||
logger.trace("New global shortcut configuration form.");
|
||||
this.initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the swing components.
|
||||
*/
|
||||
private void initComponents()
|
||||
{
|
||||
shortcutsTable.setRowHeight(22);
|
||||
shortcutsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
shortcutsTable.setShowHorizontalLines(false);
|
||||
shortcutsTable.setShowVerticalLines(false);
|
||||
shortcutsTable.setModel(tableModel);
|
||||
shortcutsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
||||
shortcutsTable.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e)
|
||||
{
|
||||
if(e.getClickCount() >= 1)
|
||||
{
|
||||
int row = GlobalShortcutConfigForm.this.shortcutsTable.
|
||||
getSelectedRow();
|
||||
int column = GlobalShortcutConfigForm.this.shortcutsTable.
|
||||
getSelectedColumn();
|
||||
if(row >= 0 && column >= 1)
|
||||
{
|
||||
currentRow = row;
|
||||
currentColumn = column;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
shortcutsTable.addKeyListener(new KeyAdapter()
|
||||
{
|
||||
private KeyEvent buffer = null;
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent event)
|
||||
{
|
||||
// Reports KEY_PRESSED events on release to support modifiers
|
||||
this.buffer = event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent event)
|
||||
{
|
||||
if (buffer != null)
|
||||
{
|
||||
AWTKeyStroke input = KeyStroke.getKeyStrokeForEvent(buffer);
|
||||
buffer = null;
|
||||
|
||||
if(currentRow != -1)
|
||||
{
|
||||
GlobalShortcutEntry en =
|
||||
GlobalShortcutConfigForm.this.tableModel.getEntryAt(
|
||||
currentRow);
|
||||
List<AWTKeyStroke> kss = new ArrayList<AWTKeyStroke>();
|
||||
|
||||
if(currentColumn == 1) // shortcut 1
|
||||
{
|
||||
kss.add(input);
|
||||
kss.add(en.getShortcut2());
|
||||
}
|
||||
else if(currentColumn == 2) // shortcut 2
|
||||
{
|
||||
kss.add(en.getShortcut());
|
||||
kss.add(input);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
en.setShortcuts(kss);
|
||||
GlobalShortcutConfigForm.this.refresh();
|
||||
GlobalShortcutConfigForm.this.saveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
scrollPane.getViewport().add(this.shortcutsTable);
|
||||
mainPanel.add(this.scrollPane, BorderLayout.CENTER);
|
||||
mainPanel.setPreferredSize(new Dimension(500, 400));
|
||||
shortcutsTable.getSelectionModel().addListSelectionListener(this);
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads configuration.
|
||||
*/
|
||||
private void loadConfig()
|
||||
{
|
||||
KeybindingsService keybindingService =
|
||||
KeybindingChooserActivator.getKeybindingsService();
|
||||
|
||||
GlobalKeybindingSet set = keybindingService.getGlobalBindings();
|
||||
|
||||
for(Map.Entry<String, List<AWTKeyStroke>> entry :
|
||||
set.getBindings().entrySet())
|
||||
{
|
||||
String key = entry.getKey();
|
||||
List<AWTKeyStroke> kss = entry.getValue();
|
||||
GlobalShortcutEntry gke = null;
|
||||
String desc = null;
|
||||
|
||||
if(key.equals("answer"))
|
||||
{
|
||||
desc = Resources.getString(
|
||||
"plugin.keybindings.globalchooser.ANSWER_CALL");
|
||||
}
|
||||
else if(key.equals("hangup"))
|
||||
{
|
||||
desc = Resources.getString(
|
||||
"plugin.keybindings.globalchooser.HANGUP_CALL");
|
||||
}
|
||||
else if(key.equals("contactlist"))
|
||||
{
|
||||
desc = Resources.getString(
|
||||
"plugin.keybindings.globalchooser.SHOW_CONTACTLIST");
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
gke = new GlobalShortcutEntry(desc, kss);
|
||||
|
||||
tableModel.addEntry(gke);
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save configuration.
|
||||
*/
|
||||
public void saveConfig()
|
||||
{
|
||||
KeybindingsService keybindingService =
|
||||
KeybindingChooserActivator.getKeybindingsService();
|
||||
GlobalShortcutService globalShortcutService =
|
||||
KeybindingChooserActivator.getGlobalShortcutService();
|
||||
Map<String, List<AWTKeyStroke>> gBindings =
|
||||
keybindingService.getGlobalBindings().getBindings();
|
||||
List<GlobalShortcutEntry> entries = tableModel.getEntries();
|
||||
List<AWTKeyStroke> kss = null;
|
||||
|
||||
for(GlobalShortcutEntry entry : entries)
|
||||
{
|
||||
String desc = null;
|
||||
|
||||
if(entry.getAction().equals(Resources.getString(
|
||||
"plugin.keybindings.globalchooser.ANSWER_CALL")))
|
||||
{
|
||||
desc = "answer";
|
||||
}
|
||||
else if(entry.getAction().equals(Resources.getString(
|
||||
"plugin.keybindings.globalchooser.HANGUP_CALL")))
|
||||
{
|
||||
desc = "hangup";
|
||||
}
|
||||
else if(entry.getAction().equals(Resources.getString(
|
||||
"plugin.keybindings.globalchooser.SHOW_CONTACTLIST")))
|
||||
{
|
||||
desc = "contactlist";
|
||||
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
kss = gBindings.get(desc);
|
||||
kss.clear();
|
||||
kss.add(entry.getShortcut());
|
||||
kss.add(entry.getShortcut2());
|
||||
gBindings.put(desc, kss);
|
||||
}
|
||||
|
||||
keybindingService.saveGlobalShortcutFromConfiguration();
|
||||
globalShortcutService.reloadGlobalShortcuts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Required by ListSelectionListener.
|
||||
*
|
||||
* @param e event triggered
|
||||
*/
|
||||
public void valueChanged(ListSelectionEvent e)
|
||||
{
|
||||
if(shortcutsTable.getSelectedRow() == -1)
|
||||
{
|
||||
currentRow = -1;
|
||||
currentColumn = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* refreshes the table display
|
||||
*/
|
||||
private void refresh()
|
||||
{
|
||||
tableModel.fireTableStructureChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this is an advanced configuration form.
|
||||
* @return <tt>true</tt> if this is an advanced configuration form,
|
||||
* otherwise it returns <tt>false</tt>
|
||||
*/
|
||||
public boolean isAdvanced()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bindings.
|
||||
*
|
||||
* @param bindings list of bindings
|
||||
*
|
||||
public void addBindings(Map<String, List<AWTKeyStroke>> bindings)
|
||||
{
|
||||
for(Map.Entry<String, List<AWTKeyStroke>> entry : bindings.entrySet())
|
||||
{
|
||||
GlobalShortcutEntry e = new GlobalShortcutEntry(
|
||||
entry.getKey(), entry.getValue());
|
||||
tableModel.addEntry(e);
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.plugin.keybindingchooser.globalchooser;
|
||||
|
||||
import java.util.List; //disambiguation
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
/**
|
||||
* Entry for a global shortcut.
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public class GlobalShortcutEntry
|
||||
{
|
||||
/**
|
||||
* Serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Disabled keystroke.
|
||||
*/
|
||||
private static final AWTKeyStroke DISABLED = null;
|
||||
|
||||
/**
|
||||
* Action name.
|
||||
*/
|
||||
private String action = null;
|
||||
|
||||
/**
|
||||
* Primary shortcut.
|
||||
*/
|
||||
private AWTKeyStroke shortcut = null;
|
||||
|
||||
/**
|
||||
* Second shortcut.
|
||||
*/
|
||||
private AWTKeyStroke shortcut2 = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param action action
|
||||
* @param shortcuts list of shortcut for this action
|
||||
*/
|
||||
public GlobalShortcutEntry(String action, List<AWTKeyStroke> shortcuts)
|
||||
{
|
||||
setAction(action);
|
||||
setShortcuts(shortcuts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns primary shortcut if it exists.
|
||||
*
|
||||
* @return primary shortcut if it exists.
|
||||
*/
|
||||
public AWTKeyStroke getShortcut()
|
||||
{
|
||||
return this.shortcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns second shortcut if it exists.
|
||||
*
|
||||
* @return second shortcut if it exists.
|
||||
*/
|
||||
public AWTKeyStroke getShortcut2()
|
||||
{
|
||||
return this.shortcut2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the shortcut keystroke and field.
|
||||
*
|
||||
* @param shortcut <tt>AWTKeyStroke</tt>
|
||||
* @return string representation of the keystroke
|
||||
*/
|
||||
public static String getShortcutText(AWTKeyStroke shortcut)
|
||||
{
|
||||
if (shortcut == DISABLED)
|
||||
{
|
||||
return "Disabled";
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
||||
if (shortcut.getKeyEventType() == KeyEvent.KEY_TYPED)
|
||||
{
|
||||
buffer.append(shortcut.getKeyChar());
|
||||
}
|
||||
else
|
||||
{
|
||||
int keycode = shortcut.getKeyCode();
|
||||
int modifiers = shortcut.getModifiers();
|
||||
|
||||
// Indicates modifiers of the keystroke
|
||||
boolean shiftMask = (modifiers & InputEvent.SHIFT_MASK) != 0;
|
||||
boolean ctrlMask = (modifiers & InputEvent.CTRL_MASK) != 0;
|
||||
boolean metaMask = (modifiers & InputEvent.META_MASK) != 0;
|
||||
boolean altMask = (modifiers & InputEvent.ALT_MASK) != 0;
|
||||
if (shiftMask && keycode != KeyEvent.VK_SHIFT)
|
||||
buffer.append("Shift + ");
|
||||
if (ctrlMask && keycode != KeyEvent.VK_CONTROL)
|
||||
buffer.append("Ctrl + ");
|
||||
if (metaMask && keycode != KeyEvent.VK_META)
|
||||
buffer.append("Meta + ");
|
||||
if (altMask && keycode != KeyEvent.VK_ALT)
|
||||
buffer.append("Alt + ");
|
||||
|
||||
buffer.append(KeyEvent.getKeyText(keycode));
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the shortcuts for this action.
|
||||
*
|
||||
* @param shortcuts list of shortcuts
|
||||
*/
|
||||
public void setShortcuts(List<AWTKeyStroke> shortcuts)
|
||||
{
|
||||
if(shortcuts.size() > 0)
|
||||
{
|
||||
this.shortcut = shortcuts.get(0);
|
||||
}
|
||||
if(shortcuts.size() > 1)
|
||||
{
|
||||
this.shortcut2 = shortcuts.get(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns action string.
|
||||
*
|
||||
* @return action
|
||||
*/
|
||||
public String getAction()
|
||||
{
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set action string
|
||||
*
|
||||
* @param action action
|
||||
*/
|
||||
public void setAction(String action)
|
||||
{
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this global keybindings is disabled.
|
||||
*
|
||||
* @return true if this global keybinding is disabled
|
||||
*/
|
||||
public boolean isDisabled()
|
||||
{
|
||||
return this.shortcut == DISABLED && this.shortcut2 == DISABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the string representation of this mapping. The exact details of
|
||||
* the representation are unspecified and subject to change but the
|
||||
* following format can be considered to be typical:<br>
|
||||
* "BindingEntry (" + Shortcut + " \u2192 " + Action + ")"
|
||||
*
|
||||
* @return string representation of entry
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("GlobalBindingEntry (");
|
||||
|
||||
if (isDisabled())
|
||||
builder.append("Disabled");
|
||||
else
|
||||
builder.append(getShortcut());
|
||||
builder.append(" \u2192 "); // arrow pointing right
|
||||
builder.append(getAction());
|
||||
builder.append(")");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if argument is an instance of this class with the same shortcut
|
||||
* and associated action. It does not compare aspects of the display
|
||||
* elements.
|
||||
*
|
||||
* @param obj element with which to be compared
|
||||
* @return true if argument is an instance of this class with matching
|
||||
* shortcut and action, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == this)
|
||||
return true;
|
||||
else if (!(obj instanceof GlobalShortcutEntry))
|
||||
return false;
|
||||
|
||||
GlobalShortcutEntry entry = (GlobalShortcutEntry) obj;
|
||||
boolean equals = true;
|
||||
|
||||
String action = this.getAction();
|
||||
if (action == null)
|
||||
equals &= entry.getAction() == null;
|
||||
else
|
||||
equals &= action.equals(entry.getAction());
|
||||
|
||||
AWTKeyStroke shortcut = this.getShortcut();
|
||||
if (shortcut == null)
|
||||
equals &= entry.getShortcut() == null;
|
||||
else
|
||||
equals &= shortcut.equals(entry.getShortcut());
|
||||
|
||||
shortcut = this.getShortcut2();
|
||||
if (shortcut == null)
|
||||
equals &= entry.getShortcut() == null;
|
||||
else
|
||||
equals &= shortcut.equals(entry.getShortcut2());
|
||||
|
||||
return equals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hashcode for this instance.
|
||||
*
|
||||
* @return hashcode for this instance
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 17;
|
||||
hash = 37 * hash + (getAction() == null ? 0 : getAction().hashCode());
|
||||
hash =
|
||||
37 * hash + (getShortcut() == null ? 0 : getShortcut().hashCode());
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.plugin.keybindingchooser.globalchooser;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.table.*;
|
||||
|
||||
/**
|
||||
* Table model for global shortcuts.
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public class GlobalShortcutTableModel
|
||||
extends AbstractTableModel
|
||||
{
|
||||
/**
|
||||
* Serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* List of shortcuts.
|
||||
*/
|
||||
private List<GlobalShortcutEntry> shortcuts =
|
||||
new ArrayList<GlobalShortcutEntry>();
|
||||
|
||||
/**
|
||||
* Returns the title for this column
|
||||
*
|
||||
* @param column the column
|
||||
*
|
||||
* @return the title for this column
|
||||
*
|
||||
* @see javax.swing.table.AbstractTableModel#getColumnName
|
||||
*/
|
||||
public String getColumnName(int column)
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case 0:
|
||||
return Resources.getString(
|
||||
"plugin.keybindings.globalchooser.SHORTCUT_NAME");
|
||||
case 1:
|
||||
return Resources.getString(
|
||||
"plugin.keybindings.globalchooser.SHORTCUT_PRIMARY");
|
||||
case 2:
|
||||
return Resources.getString(
|
||||
"plugin.keybindings.globalchooser.SHORTCUT_SECOND");
|
||||
default:
|
||||
throw new IllegalArgumentException("column not found");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows in the table
|
||||
*
|
||||
* @return the number of rows in the table
|
||||
* @see javax.swing.table.AbstractTableModel#getRowCount
|
||||
*/
|
||||
public int getRowCount()
|
||||
{
|
||||
return shortcuts.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of column in the table
|
||||
*
|
||||
* @return the number of columns in the table
|
||||
*
|
||||
* @see javax.swing.table.AbstractTableModel#getColumnCount
|
||||
*/
|
||||
public int getColumnCount()
|
||||
{
|
||||
// 3 columns: "name", "primary shortcut", "second shortcut"
|
||||
return 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text for the given cell of the table
|
||||
*
|
||||
* @param row cell row
|
||||
* @param column cell column
|
||||
* @return object at the row/column
|
||||
* @see javax.swing.table.AbstractTableModel#getValueAt
|
||||
*/
|
||||
public Object getValueAt(int row, int column)
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case 0:
|
||||
return getEntryAt(row).getAction();
|
||||
case 1:
|
||||
return GlobalShortcutEntry.getShortcutText(
|
||||
getEntryAt(row).getShortcut());
|
||||
case 2:
|
||||
return GlobalShortcutEntry.getShortcutText(
|
||||
getEntryAt(row).getShortcut2());
|
||||
default:
|
||||
throw new IllegalArgumentException("column not found");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LdapDirectory at the row 'row'
|
||||
*
|
||||
* @param row the row on which to find the LdapDirectory
|
||||
*
|
||||
* @return the LdapDirectory found
|
||||
*/
|
||||
public GlobalShortcutEntry getEntryAt(int row)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for(GlobalShortcutEntry entry : shortcuts)
|
||||
{
|
||||
if(i == row)
|
||||
return entry;
|
||||
i++;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("row not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a cell is editable.
|
||||
* @param row row of the cell
|
||||
* @param col column of the cell
|
||||
*
|
||||
* @return whether the cell is editable
|
||||
*/
|
||||
public boolean isCellEditable(int row, int col)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides a method that always returned Object.class
|
||||
* Now it will return Boolean.class for the first method,
|
||||
* letting the DefaultTableCellRenderer create checkboxes.
|
||||
*
|
||||
* @param columnIndex index of the column
|
||||
* @return Column class
|
||||
*/
|
||||
public Class<?> getColumnClass(int columnIndex)
|
||||
{
|
||||
Object o = getValueAt(0, columnIndex);
|
||||
if(o == null)
|
||||
return String.class;
|
||||
return o.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in an editable cell.
|
||||
*
|
||||
* @param aValue value to set
|
||||
* @param rowIndex row index
|
||||
* @param columnIndex column index
|
||||
*/
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
|
||||
{
|
||||
if(columnIndex != 0)
|
||||
throw new IllegalArgumentException("non editable column!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entry.
|
||||
*
|
||||
* @param entry entry to add
|
||||
*/
|
||||
public void addEntry(GlobalShortcutEntry entry)
|
||||
{
|
||||
shortcuts.add(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entry.
|
||||
*
|
||||
* @param entry entry to add
|
||||
*/
|
||||
public void removeEntry(GlobalShortcutEntry entry)
|
||||
{
|
||||
shortcuts.remove(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all shortcuts.
|
||||
*
|
||||
* @return all shortcuts.
|
||||
*/
|
||||
public List<GlobalShortcutEntry> getEntries()
|
||||
{
|
||||
return shortcuts;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.plugin.keybindingchooser.globalchooser;
|
||||
|
||||
import net.java.sip.communicator.plugin.keybindingchooser.*;
|
||||
|
||||
/**
|
||||
* The <tt>Resources</tt> class manages the access to the internationalization
|
||||
* properties files and the image resources used in this plugin.
|
||||
*
|
||||
* @author Yana Stamcheva
|
||||
*/
|
||||
public class Resources
|
||||
{
|
||||
/**
|
||||
* Returns an internationalized string corresponding to the given key.
|
||||
*
|
||||
* @param key The key of the string.
|
||||
* @return An internationalized string corresponding to the given key.
|
||||
*/
|
||||
public static String getString(String key)
|
||||
{
|
||||
return KeybindingChooserActivator.getResources()
|
||||
.getI18NString(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an internationalized string corresponding to the given key.
|
||||
*
|
||||
* @param key The key of the string.
|
||||
* @param params additionnal parameters
|
||||
* @return An internationalized string corresponding to the given key.
|
||||
*/
|
||||
public static String getString(String key, String[] params)
|
||||
{
|
||||
return KeybindingChooserActivator.getResources()
|
||||
.getI18NString(key, params);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.service.globalshortcut;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Event related to global shortcut.
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public class GlobalShortcutEvent
|
||||
{
|
||||
/**
|
||||
* Key stroke.
|
||||
*/
|
||||
private final AWTKeyStroke keyStroke;
|
||||
|
||||
/**
|
||||
* Initializes a new <tt>GlobalShortcutEvent</tt>.
|
||||
*
|
||||
* @param keyStroke keystroke
|
||||
*/
|
||||
public GlobalShortcutEvent(AWTKeyStroke keyStroke)
|
||||
{
|
||||
this.keyStroke = keyStroke;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns keyStroke.
|
||||
*
|
||||
* @return keystroke
|
||||
*/
|
||||
public AWTKeyStroke getKeyStroke()
|
||||
{
|
||||
return keyStroke;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.service.globalshortcut;
|
||||
|
||||
/**
|
||||
* Global shortcut listener.
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public interface GlobalShortcutListener
|
||||
{
|
||||
/**
|
||||
* Callback when an shortcut is typed
|
||||
*
|
||||
* @param evt <tt>GlobalShortcutEvent</tt>
|
||||
*/
|
||||
public void shortcutReceived(GlobalShortcutEvent evt);
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.service.globalshortcut;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* This global shortcut service permits to register listeners for global
|
||||
* shortcut (i.e. keystroke even if application is not foreground).
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public interface GlobalShortcutService
|
||||
{
|
||||
/**
|
||||
* Registers an action to execute when the keystroke is typed.
|
||||
*
|
||||
* @param l listener to notify when keystroke is typed
|
||||
* @param keyStroke keystroke that will trigger the action
|
||||
*/
|
||||
public void registerShortcut(GlobalShortcutListener l,
|
||||
AWTKeyStroke keyStroke);
|
||||
|
||||
/**
|
||||
* Unregisters an action to execute when the keystroke is typed.
|
||||
*
|
||||
* @param l listener to remove
|
||||
* @param keyStroke keystroke that will trigger the action
|
||||
*/
|
||||
public void unregisterShortcut(GlobalShortcutListener l,
|
||||
AWTKeyStroke keyStroke);
|
||||
|
||||
/**
|
||||
* Reload global shortcuts.
|
||||
*/
|
||||
public void reloadGlobalShortcuts();
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
||||
*
|
||||
* Distributable under LGPL license.
|
||||
* See terms of license at gnu.org.
|
||||
*/
|
||||
package net.java.sip.communicator.service.keybindings;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.List; //disambiguation
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Global keybinding set interface.
|
||||
*
|
||||
* @author Sebastien Vincent
|
||||
*/
|
||||
public interface GlobalKeybindingSet
|
||||
{
|
||||
/**
|
||||
* Provides current keybinding mappings.
|
||||
* @return mapping of keystrokes to the string representation of the actions
|
||||
* they perform
|
||||
*/
|
||||
public Map<String, List<AWTKeyStroke>> getBindings();
|
||||
|
||||
/**
|
||||
* Resets the bindings and notifies the observer's listeners if they've
|
||||
* changed.
|
||||
* @param bindings new keybindings to be held
|
||||
*/
|
||||
public void setBindings(Map<String, List<AWTKeyStroke>> bindings);
|
||||
}
|
||||
Loading…
Reference in new issue