Show the modifier key presses in the log file - objective-c

I'm trying to modify this code. It's a logging script that shows keypresses.
Currently the log only shows the ABCs, numbers, and a combination of modifier+(key). Is it possible to show every keypress like "command", "option", "F1" and so on?
I don't know Objective-C and I'm blindly trying to change this code for a project of mine. Any help will take me a step further in this project and is much appreciated.
The repo:
https://github.com/chbrown/osx-tap
My version that shows the actual keys names:
#import <Foundation/Foundation.h>
#include <ApplicationServices/ApplicationServices.h>
#import "key-tap.h"
#implementation LoggerConfig
#synthesize output;
#synthesize epoch;
#end
/* CGEventTapCallBack function signature:
proxy
A proxy for the event tap. See CGEventTapProxy. This callback function may pass this proxy to other functions such as the event-posting routines.
type
The event type of this event. See “Event Types.”
event
The incoming event. This event is owned by the caller, and you do not need to release it.
refcon
A pointer to user-defined data. You specify this pointer when you create the event tap. Several different event taps could use the same callback function, each tap with its own user-defined data.
*/
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
LoggerConfig *config = (__bridge LoggerConfig *)refcon;
#autoreleasepool {
NSMutableArray *modifiers = [NSMutableArray arrayWithCapacity:10];
CGEventFlags flags = CGEventGetFlags(event);
if ((flags & kCGEventFlagMaskShift) == kCGEventFlagMaskShift)
[modifiers addObject:#"shift"];
if ((flags & kCGEventFlagMaskControl) == kCGEventFlagMaskControl)
[modifiers addObject:#"control"];
if ((flags & kCGEventFlagMaskAlternate) == kCGEventFlagMaskAlternate)
[modifiers addObject:#"alt"];
if ((flags & kCGEventFlagMaskCommand) == kCGEventFlagMaskCommand)
[modifiers addObject:#"command"];
if ((flags & kCGEventFlagMaskSecondaryFn) == kCGEventFlagMaskSecondaryFn)
[modifiers addObject:#"fn"];
// Ignoring the following flags:
// kCGEventFlagMaskAlphaShift = NX_ALPHASHIFTMASK,
// kCGEventFlagMaskHelp = NX_HELPMASK,
// kCGEventFlagMaskNumericPad = NX_NUMERICPADMASK,
// kCGEventFlagMaskNonCoalesced = NX_NONCOALSESCEDMASK
NSString *modifierString = [modifiers componentsJoinedByString:#"+"];
// The incoming keycode. CGKeyCode is just a typedef of uint16_t, so we treat it like an int
CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
// CGEventKeyboardGetUnicodeString
// Keypress code goes here.
NSString *action;
if (type == kCGEventKeyDown)
action = #"down";
else if (type == kCGEventKeyUp)
action = #"up";
else
action = #"other";
NSString *keytype;
switch (keycode) {
case 0: keytype = #"a"; break;
case 1: keytype = #"s"; break;
case 2: keytype = #"d"; break;
case 3: keytype = #"f"; break;
case 4: keytype = #"h"; break;
case 5: keytype = #"g"; break;
case 6: keytype = #"z"; break;
case 7: keytype = #"x"; break;
case 8: keytype = #"c"; break;
case 9: keytype = #"v"; break;
case 11: keytype = #"b"; break;
case 12: keytype = #"q"; break;
case 13: keytype = #"w"; break;
case 14: keytype = #"e"; break;
case 15: keytype = #"r"; break;
case 16: keytype = #"y"; break;
case 17: keytype = #"t"; break;
case 18: keytype = #"1"; break;
case 19: keytype = #"2"; break;
case 20: keytype = #"3"; break;
case 21: keytype = #"4"; break;
case 22: keytype = #"6"; break;
case 23: keytype = #"5"; break;
case 24: keytype = #"#"; break;
case 25: keytype = #"9"; break;
case 26: keytype = #"7"; break;
case 27: keytype = #"-"; break;
case 28: keytype = #"8"; break;
case 29: keytype = #"0"; break;
case 30: keytype = #"]"; break;
case 31: keytype = #"o"; break;
case 32: keytype = #"u"; break;
case 33: keytype = #"["; break;
case 34: keytype = #"i"; break;
case 35: keytype = #"p"; break;
case 37: keytype = #"l"; break;
case 38: keytype = #"j"; break;
case 39: keytype = #"'"; break;
case 40: keytype = #"k"; break;
case 41: keytype = #";"; break;
case 42: keytype = #"\\"; break;
case 43: keytype = #","; break;
case 44: keytype = #"/"; break;
case 45: keytype = #"n"; break;
case 46: keytype = #"m"; break;
case 47: keytype = #"."; break;
case 50: keytype = #"`"; break;
case 65: keytype = #"[decimal]"; break;
case 67: keytype = #"[asterisk]"; break;
case 69: keytype = #"[plus]"; break;
case 71: keytype = #"[clear]"; break;
case 75: keytype = #"[divide]"; break;
case 76: keytype = #"[enter]"; break;
case 78: keytype = #"[hyphen]"; break;
case 81: keytype = #"[equals]"; break;
case 82: keytype = #"0"; break;
case 83: keytype = #"1"; break;
case 84: keytype = #"2"; break;
case 85: keytype = #"3"; break;
case 86: keytype = #"4"; break;
case 87: keytype = #"5"; break;
case 88: keytype = #"6"; break;
case 89: keytype = #"7"; break;
case 91: keytype = #"8"; break;
case 92: keytype = #"9"; break;
case 36: keytype = #"[return]"; break;
case 48: keytype = #"[tab]"; break;
case 49: keytype = #" "; break;
case 51: keytype = #"[del]"; break;
case 53: keytype = #"[esc]"; break;
case 54: keytype = #"[right-cmd]"; break;
case 55: keytype = #"[left-cmd]"; break;
case 56: keytype = #"[left-shift]"; break;
case 57: keytype = #"[caps]"; break;
case 58: keytype = #"[left-option]"; break;
case 59: keytype = #"[left-ctrl]"; break;
case 60: keytype = #"[right-shift]"; break;
case 61: keytype = #"[right-option]"; break;
case 62: keytype = #"[right-ctrl]"; break;
case 63: keytype = #"[fn]"; break;
case 64: keytype = #"[f17]"; break;
case 72: keytype = #"[volup]"; break;
case 73: keytype = #"[voldown]"; break;
case 74: keytype = #"[mute]"; break;
case 79: keytype = #"[f18]"; break;
case 80: keytype = #"[f19]"; break;
case 90: keytype = #"[f20]"; break;
case 96: keytype = #"[f5]"; break;
case 97: keytype = #"[f6]"; break;
case 98: keytype = #"[f7]"; break;
case 99: keytype = #"[f3]"; break;
case 100: keytype = #"[f8]"; break;
case 101: keytype = #"[f9]"; break;
case 103: keytype = #"[f11]"; break;
case 105: keytype = #"[f13]"; break;
case 106: keytype = #"[f16]"; break;
case 107: keytype = #"[f14]"; break;
case 109: keytype = #"[f10]"; break;
case 111: keytype = #"[f12]"; break;
case 113: keytype = #"[f15]"; break;
case 114: keytype = #"[help]"; break;
case 115: keytype = #"[home]"; break;
case 116: keytype = #"[pgup]"; break;
case 117: keytype = #"[fwddel]"; break;
case 118: keytype = #"[f4]"; break;
case 119: keytype = #"[end]"; break;
case 120: keytype = #"[f2]"; break;
case 121: keytype = #"[pgdown]"; break;
case 122: keytype = #"[f1]"; break;
case 123: keytype = #"[left]"; break;
case 124: keytype = #"[right]"; break;
case 125: keytype = #"[down]"; break;
case 126: keytype = #"[up]"; break;
default: keytype = #"none"; break;
}
NSTimeInterval offset = [[NSDate date] timeIntervalSince1970] - config.epoch;
// logLine format:
// ticks since started <TAB> key code <TAB> action <TAB> modifiers
// so it'll look something like "13073 45 up shift+command"
NSString *logLine = [NSString stringWithFormat:#"%d\t%d\t%#\t%#\t%#\n",
(int)offset, keycode ,keytype, action, modifierString];
NSLog(#"> %#", logLine);
[config.output writeData:[logLine dataUsingEncoding:NSUTF8StringEncoding]];
}
// We must return the event for it to be useful.
return event;
}
int main(void) {
// set up the file that we'll be logging into
// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
LoggerConfig *config = [[LoggerConfig alloc] init];
NSUserDefaults *args = [NSUserDefaults standardUserDefaults];
// grabs command line arguments --directory
NSString *directory = [args stringForKey:#"directory"];
if (!directory) {
// default to /var/log/osx-tap
directory = #"/var/log/osx-tap";
}
NSFileManager *fileManager = [NSFileManager defaultManager];
// create directory if needed (since withIntermediateDirectories:YES,
// this shouldn't fail if the directory already exists)
bool directory_success = [fileManager createDirectoryAtPath:directory
withIntermediateDirectories:YES attributes:nil error:nil];
if (!directory_success) {
NSLog(#"Could not create directory: %#", directory);
return 1;
}
config.epoch = [[NSDate date] timeIntervalSince1970];
NSString *filename = [NSString stringWithFormat:#"%d.log", (int)config.epoch];
NSString *filepath = [NSString pathWithComponents:#[directory, filename]];
bool create_success = [fileManager createFileAtPath:filepath contents:nil attributes:nil];
if (!create_success) {
NSLog(#"Could not create file: %#", filepath);
return 1;
}
// now that it's been created, we can open the file
config.output = [NSFileHandle fileHandleForWritingAtPath:filepath];
// [config.output seekToEndOfFile];
// Create an event tap. We are interested in key ups and downs.
CGEventMask eventMask = ((1 << kCGEventKeyDown) | (1 << kCGEventKeyUp));
/*
CGEventTapCreate(CGEventTapLocation tap, CGEventTapPlacement place,
CGEventTapOptions options, CGEventMask eventsOfInterest,
CGEventTapCallBack callback, void *refcon
CGEventTapLocation tap:
kCGHIDEventTap
Specifies that an event tap is placed at the point where HID system events enter the window server.
kCGSessionEventTap
Specifies that an event tap is placed at the point where HID system and remote control events enter a login session.
kCGAnnotatedSessionEventTap
Specifies that an event tap is placed at the point where session events have been annotated to flow to an application.
CGEventTapPlacement place:
kCGHeadInsertEventTap
Specifies that a new event tap should be inserted before any pre-existing event taps at the same location.
kCGTailAppendEventTap
Specifies that a new event tap should be inserted after any pre-existing event taps at the same location.
CGEventTapOptions options:
kCGEventTapOptionDefault = 0x00000000
kCGEventTapOptionListenOnly = 0x00000001
...
CGEventTapCallBack has arguments:
(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
*/
// we don't want to discard config
// CFBridgingRetain(config);
CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0,
eventMask, myCGEventCallback, (__bridge void *)config);
if (!eventTap) {
NSLog(#"failed to create event tap");
return 1;
}
if (!CGEventTapIsEnabled(eventTap)) {
NSLog(#"event tap is not enabled");
return 1;
}
/* Create a run loop source.
allocator
The allocator to use to allocate memory for the new object. Pass NULL or kCFAllocatorDefault to use the current default allocator.
port
The Mach port for which to create a CFRunLoopSource object.
order
A priority index indicating the order in which run loop sources are processed. order is currently ignored by CFMachPort run loop sources. Pass 0 for this value.
*/
CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
/* Adds a CFRunLoopSource object to a run loop mode.
CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
rl
The run loop to modify.
source
The run loop source to add. The source is retained by the run loop.
mode
The run loop mode to which to add source. Use the constant kCFRunLoopCommonModes to add source to the set of objects monitored by all the common modes.
*/
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
// Enable the event tap.
// CGEventTapEnable(eventTap, true);
// Runs the current thread’s CFRunLoop object in its default mode indefinitely:
CFRunLoopRun();
return 0;
}
I've tried to understand this function but failed to do so after about 3 hours of fiddling.

Related

How to get actual name of Mac Operating System instead of version?

I am able to get the version of OSX by using the code given below however what I want is the name of operating system (using Objective C).
Thanks in advance!!!
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
NSString* major = [NSString stringWithFormat:#"%d", version.majorVersion];
NSString* minor = [NSString stringWithFormat:#"%d", version.minorVersion];
NSString* patch = [NSString stringWithFormat:#"%d", version.patchVersion];
There is no API that I know of that would produce the product name of the current OS version. Even grepping for the product name in system locations yields surprisingly few results, and most of those in private frameworks. The only promising non-private match I found is in the Setup Assistant.app, and requires a horrible kludge to extract from a longer string:
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:#"/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/Localizable.strings"];
NSString *productName = [dict objectForKey:#"INSTALLATION_COMPLETE"];
if (productName) {
NSRange r = [productName rangeOfString:#" has been"];
if (r.location != NSNotFound) {
productName = [productName substringToIndex:r.location];
} else {
productName = nil;
}
}
This happens to work for Yosemite and El Capitan, and produces "OS X Yosemite" and "OS X El Capitan". But even with these two versions the kludgey nature reveals itself; the El Capitan string contains non-breakable spaces…
Apart from this (or a similar kludge using other files not meant to be used this way), one can of course obtain the numeric version and match it against a list of known product names, which would be my recommended solution, perhaps with the above kludge as a fallback.
Silly, but this should work.
public extension ProcessInfo {
func osName() -> String? {
let version = self.operatingSystemVersion
switch version.minorVersion {
case 15: return "Catalina"
case 14: return "Mojave"
case 13: return "High Sierra"
case 12: return "Sierra"
case 11: return "El Capitan"
case 10: return "Yosemite"
case 9: return "Mavericks"
case 8: return "Mountain Lion"
case 7: return "Lion"
case 6: return "Snow Leopard"
case 5: return "Leopard"
case 4: return "Tiger"
case 3: return "Panther"
case 2: return "Jaguar"
case 1: return "Puma"
case 0: return "Kodiak"
default: return nil
}
}
for the sake of one more option to figure out the OSX Name i am providing a work-around below:
// this function will return os name
-(NSString *)getOSName{
// get major version
NSString* majorVersion = [self getMajorVersion];
// get minor version
NSString* minorVersion= [self getMinorVersion];
NSString* OSName = #"";
// figure out the name using versions
if([minorVersion isEqualTo:#"11"])
{
OSName = #"El Capitan";
}
else if([minorVersion isEqualTo:#"10"])
{
OSName = #"Yosemite";
}
else if([minorVersion isEqualTo:#"9"])
{
OSName = #"Mavericks";
}
else if([minorVersion isEqualTo:#"8"])
{
OSName = #"Mountain Lion";
}
else if([minorVersion isEqualTo:#"7"])
{
OSName = #"Lion";
}
else if([minorVersion isEqualTo:#"6"])
{
OSName = #"Snow Leopard";
}
else if([minorVersion isEqualTo:#"5"])
{
OSName = #"Leopard";
}
else if([minorVersion isEqualTo:#"4"])
{
OSName = #"Tiger";
}
else if([minorVersion isEqualTo:#"3"])
{
OSName = #"Panther";
}
else if([minorVersion isEqualTo:#"2"])
{
OSName = #"Jaguar";
}
else if([minorVersion isEqualTo:#"1"])
{
OSName = #"Puma";
}
else if([minorVersion isEqualTo:#"0"])
{
OSName = #"Kodiak";
}
return OSName;
}
-(NSString*)getMajorVersion{
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
NSString* ver = [NSString stringWithFormat:#"%d", version.majorVersion];
return ver;
}
-(NSString*)getMinorVersion{
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
NSString* ver = [NSString stringWithFormat:#"%d", version.minorVersion];
return ver;
}

Error: index 1 beyond bounds for empty array

I have created an NSMutableArray to store some character there. When I'am adding some character there it gives me an error. Here my code:
#property(strong,nonatomic) NSMutableArray *actions; < ViewController.h
#synthesize actions; < ViewController.m
actions = [[NSMutableArray alloc]init];
switch ([sender tag]) {
case 0:
currentLabel = water;
[actions insertObject:#"0" atIndex:i];
i++;
break;
case 1:
currentLabel = coke;
[actions insertObject:#"1" atIndex:i];
i++;
break;
case 2:
currentLabel = fanta;
[actions insertObject:#"2" atIndex:i];
i++;
break;
}
Use [actions addObject:#"X"] instead. If the array does not have elements, you can't insert object at index 1.

Switch on an enum

I have the following piece of code:
switch (self.status) {
case refused:
message=NSLocalizedString(#"booking refused", nil);
break;
case accepted:
message=NSLocalizedString(#"booking accepted", nil);
break;
case taxiAborted:
message=NSLocalizedString(#"booking aborted", nil);
break;
case near:
message=NSLocalizedString(#"booking aborted", nil);
break;
case completed:
message=NSLocalizedString(#"booking completed", nil);
break;
default:
break;
}
that never enters the interior of the switch: neither hitting the default case nor the correct value. In my case self.status had value 'active' that is part of the following enumeration:
typedef enum {
requested = 0,
refused = 1,
accepted =2,
taxiAborted=3,
near = 4,
running= 5,
completed = 6,
userAborted=7
} statuses;
What did I do wrong?

Convert inputstream to textfield.text objective-c

I have some problem with my objective-c app. I want to convert input stream to textfield.text in objective-c.
I have tcp client with input stream and output stream. I want to use data from input stream and show them in uitextfield.
Can I use something like that:
temperatura.text = [inputStream read:buffer maxLength:sizeof(buffer)];
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %i", streamEvent);
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream) {
uint8_t buffer[1024];
int len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
NSLog(#"server said: %#", output);
}
}
}
}
break;
I'm using this client. I want to use data from 'output' to get them on textfield.
Following line of code will return an int
[inputStream read:buffer maxLength:sizeof(buffer)]
Following line of code needs a NSString not int
temperatura.text
So you will have to typecast [inputStream read:buffer maxLength:sizeof(buffer)] to a string before you assign to temperatura.text . You can use following to typecast:
temperatura.text = [NSString stringWithFormat:#"%d",[inputStream read:buffer maxLength:sizeof(buffer)]];
A simple typecast example is :
NSString *string = [NSString stringWithFormat:#"%d", theinteger];
Please note that I have not tested this code for proper syntax, but this could be a solution. Let me know if you face any problems.
Thanks
Refer Folowing Code
(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
if(!_data) {
_data = [[NSMutableData data] retain];
}
uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if(len) {
[_data appendBytes:(const void *)buf length:len];
// bytesRead is an instance variable of type NSNumber.
[bytesRead setIntValue:[bytesRead intValue]+len];
} else {
NSLog(#"no buffer!");
}
break;
**
temperatura.text = [[NSString alloc] initWithBytes:[_data bytes] length:[_data length] encoding:NSUTF8StringEncoding]
**

Issue with NSString and concatenation

Why this code gives me the following errors?
"use of undeclared identifier baseURL"
and
"Unexpected Interface name NSString, expected expression"
here is the entire block of code
switch (type) {
case 1:
NSString *baseURL = [NSString stringWithString:#"http://www.myserver.net/somephp/"];
NSString *finalURL = [baseURL stringByAppendingString:#"?i="];
break;
case 2:
NSString *finalURL = [baseURL stringByAppendingString:#"?n="];
break;
default:
break;
}
Sounds like those lines are within a switch statement. If this is the case, move the declaration of the strings outside the switch statement.
NSString *baseURL;
NSString *finalURL;
switch (<expression>) {
case <constant>:
baseURL = [NSString stringWithString:#"http://www.myserver.net/somephp"];
finalURL = [baseURL stringByAppendingString:#"?i="];
break;
default:
break;
}
More information and other techniques to work around this on this question.