1

I want to get keyboard layout something like GetKeyboardLayout(threadId) in Windows. threadId is id of application with active window. I want to translate virtual key code to char of current language.

Now I use TISCopyCurrentKeyboardInputSource() function but it don't have parameters and it return only "U. S." language. I think this is layout of daemon. Also I can't convert code with TISInputSourceRef of TISCopyInputSourceForLanguage(language) function. It return only US characters.

UPDATE: Okey. I find solution of converting keyCodes. I jast call function UCKeyTranslate with parameter modifiers equal 0. But I cant't find how to get input source of active window or just any running application.

UPDATE 2: plist path is /Library/LaunchAgents

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>my.keylogger</string>
    <key>Program</key>
    <string>/Users/Titan/Desktop/keylogger</string>
    <key>ProgramArguments</key>
    <array>
      <string>keylogger</string>
    </array>
    <key>KeepAlive</key>
    <true/>
  </dict>
</plist>

1 Answers1

1

Whenever you want to access the state of user sessions from a daemon, You're Doing It Wrong™. It can't be made to work reliably.

The standard solution is to use user agents that get launched in each user session. Each agent can contact a central daemon and report about the state of the user session, if necessary.

See Technical Note TN2083: Daemons and Agents for more details.


Update:

The above technote is relevant for understanding why you can't do this from a daemon.

For the agent, it should use the distributed notification center to observe the kTISNotifySelectedKeyboardInputSourceChanged notification to learn when the selected keyboard input source changes. Then it can call TISCopyCurrentKeyboardInputSource() to learn what keyboard input source is current.

In order for the agent to receive the notification, it must run the run loop of its main thread in one of the common modes. In a normal Cocoa app, this is done for you as part of the main event loop. In a different kind of program, you have to do this yourself.

BOOL shouldKeepRunning = YES;        // global
...

[[NSDistributedNotificationCenter defaultCenter] addObserver:self
                                                    selector:@selector(myMethod:)
                                                        name:(__bridge NSString*)kTISNotifySelectedKeyboardInputSourceChanged
                                                      object:nil
                                          suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];

while (shouldKeepRunning && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
    /* do nothing */;

...
- (void) myMethod:(NSNotification*)note
{
    TISInputSourceRef inputSource = TISCopyCurrentKeyboardInputSource();
    // do something with inputSource ...
}
Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Thank you. I'll look in this direction and then write my results. – Maksim Myshkin Nov 02 '14 at 08:42
  • I have read this article but I still don't understand how to get the input source of frontmost application. It is my first program of this kind and I don't know what use for this purpose. Program must just to write a log for each user. It doesn't need to communicate with central daemon (as I think). – Maksim Myshkin Nov 02 '14 at 11:43
  • I've updated my answer with further explanation and example code – Ken Thomases Nov 02 '14 at 17:38
  • I think I have understood the idea. I will check this solution and write thanks for you tomorrow) – Maksim Myshkin Nov 02 '14 at 20:15
  • It work fine when I run it as console application but when I run this as agent it ruturn only U. S. Maybe am I somehow wrong run agent? – Maksim Myshkin Nov 03 '14 at 08:18
  • What `launchctl` command did you use to load the agent? Did you use `sudo`? You shouldn't need to do that for user agents and it will probably do the wrong thing. What are the details for the agent process as shown by Activity Monitor? – Ken Thomases Nov 03 '14 at 12:53
  • Thank you so much. It all because I use `sudo` with `launchctl`. Now all work fine. – Maksim Myshkin Nov 03 '14 at 15:07