星期五, 8月 01, 2008

java.awt.Toolkit.getLockingKeyState on Linux

最近終於突破一個SUN JVM在Linux下的限制. 雖然只是一件小事. 就是在Linux X Window的環境下取得鍵盤Caps Lock, Num Lock, Scroll Lock目前的狀態. 看起來很簡單的小事, 可是卻花了我很久的時間才知道要怎麼做.

首先講在MS Windows下的做法, 很簡單, 利用java.awt.Toolkit就可以簡單的做到.

public class KeyLockDemo {
  public static void main(String[] args) {
    java.awt.Toolkit tk = java.awt.Toolkit.getDefaultToolkit();
    System.out.println("Caps Lock is " + (tk.getLockingKeyState(java.awt.event.KeyEvent.VK_CAPS_LOCK) ? "on" : "off"));
    System.out.println("Num Lock is " + (tk.getLockingKeyState(java.awt.event.KeyEvent.VK_NUM_LOCK) ? "on" : "off"));
    System.out.println("Scroll Lock is " + (tk.getLockingKeyState(java.awt.event.KeyEvent.VK_SCROLL_LOCK) ? "on" : "off"));
  }
}

非常簡單...

但同樣的code在Linux下卻不行用, 因為SUN JVM在Linux下的java.awt.Toolkit沒有實作getLockingKeyState這個method, 因此會丟出UnsupportedOperationException.

哪怎麼辦呢? 其中一條路是利用JNI (我也只知道這條路).

首先引用我在網路上找到的資料, 在[1]中提到三個方法可以"控制小鍵盤及鍵盤燈狀態".

第一個方法是利用ioctl的方式存取stdin以獲得狀態, 最理想的方法, 但僅能在console的模式下使用, 即進入X Window後就不能用了.

第二個方法我個人沒試過, 但它是利用X11的函式庫實作.

第三個方法則是利用ioperm的方式直接操作鍵盤燈, 但仍無法取得locks目前的狀態. 附帶一提, 這個方法其實是下Keyboard Command達成控制LED的, 可以參考[2].

咦? 這樣不就又沒解了嗎? 是的, 所以, 我現在要講的是怎麼修改第一個方法使得在X Window下也能取得locks的狀態.

首先, 第一種方法是利用stdin這個file descriptor[3]來實作. 但在X Window下, stdin似乎被別的input device取代了, 所以對stdin做ioctl也無法取得狀態. 所以, 我們要自己open在console mode下的stdin (i.e., /dev/tty0 or /dev/tty1). Source code如下:

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/kd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#define NUMLOCK 0x02
#define CAPS 0X04
#define SCROLL 0X01

int main(int argc,char **argv) {
  int fd = 0;
  unsigned char leds = 0;

  fd = open("/dev/tty1", O_RDONLY);
  ioctl(fd, KDGETLED, &leds);

  printf("Caps Lock is %s\n", (leds & CAPS ? "on" : "off"));
  printf("Num Lock is %s\n", (leds & NUMLOCK ? "on" : "off"));
  printf("Scroll Lock is %s\n", (leds & SCROLL ? "on" : "off"));
}



方法有了, 剩下就是自己在Java裡面寫個JNI method呼叫C的function把狀態取回來吧!!!

[1] 控制小鍵盤及鍵盤燈狀態, http://blog.csdn.net/cceczjxy/archive/2008/06/25/2584796.aspx

[2] Keyboard scancodes, http://www.win.tue.nl/~aeb/linux/kbd/scancodes.html

[3] File descriptor, http://en.wikipedia.org/wiki/File_descriptor

2 則留言:

Hahaha 提到...

我有問題!! (舉手
為什麼不直接用X11的方式實作 (爆)

Bobby 提到...

之前就為了取得locks的狀態, 曾看過一下X11 library...覺得用X11 library好複雜了啊...也沒有啦...其實是自己懶

更重要的是, ioctl簡單又好用...XD