Getting global keystrokes in a Java or Scala Swing application

If you ever wanted to get access to global operating system keystrokes from a Java or Scala Swing application, you can do it with the jnativehook library. Here’s a short demo:

Here’s the full Scala source code for the demo. I put a few comments in the code to highlight the important areas:

package com.alvinalexander.mackeystrokes

import scala.swing._
import scala.swing.event.EditDone
import scala.swing.event.WindowActivated
import org.jnativehook.GlobalScreen
import org.jnativehook.NativeHookException
import org.jnativehook.keyboard.NativeKeyEvent
import org.jnativehook.keyboard.NativeKeyListener
import javax.swing.SwingUtilities

import java.awt.Graphics
import javax.swing.JPanel
import java.awt.Color

// (1) extend NativeKeyListener
object MacKeystrokes 
extends SimpleSwingApplication 
with NativeKeyListener 

  val charPanel = new CharPanel {
    setPreferredSize(new Dimension(400, 300))
  def top = new MainFrame { 
    title = "Mac Keystrokes"

  // (2) GlobalScreen.registerNativeHook
  try {
  } catch {
    case e: NativeHookException =>
          System.err.println("There was a problem registering the native hook.")

  // (3) implement nativeKeyPressed, nativeKeyReleased, and nativeKeyTyped
  def nativeKeyPressed(e: NativeKeyEvent) {
    val key = NativeKeyEvent.getKeyText(e.getKeyCode)
    println("Key Pressed: " + NativeKeyEvent.getKeyText(e.getKeyCode))
    SwingUtilities.invokeLater(new Runnable {
      def run {
    if (e.getKeyCode == NativeKeyEvent.VK_ESCAPE) {

  def nativeKeyReleased(e: NativeKeyEvent) {}
  def nativeKeyTyped(e: NativeKeyEvent) {}

  // (4) do this


import scala.util.Random
import java.awt.Font

class CharPanel extends JPanel {

  private val colors = Array(Color.BLUE, Color.CYAN, Color.DARK_GRAY, 
                             Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, 
                             Color.ORANGE, Color.RED, Color.YELLOW)
  private var chars = Array('a')
  private val r = new Random
  var i = 10
  def setChar(charArray: Array[Char]) { 
    this.chars = charArray
    i += 15
  override def paintComponent(g: Graphics) {
    val font = new Font(Font.SANS_SERIF, Font.PLAIN, 60)
    val color = colors(r.nextInt(colors.length))
    g.clearRect(0, 0, 400, 300)
    g.drawChars(chars, 0, 1, r.nextInt(320), r.nextInt(220)+20)


I needed to be able to do something like this for SARAH, so I dug around until I found this solution, which works great.

To get this to work, you have to enable “access for assistive devices”, as explained in this article.

Again, I did remarkably little here; the secret sauce is in the jnativehook library. See their examples and docs for more information.