X Window研究筆記(6)
6.X Window輸入裝置(TinyX)
X Window支援的基本輸入裝置有keyboard、mouse和touchscreen,keyboard有自己的驅動接口,而後兩者具有相同的驅動接口。
輸入裝置的初始化。
- X Server在初始化時會調用InitInput函數初始化輸入裝置。
- InitInput調用KdInitInput完成TinyX輸入裝置的初始化。其參數LinuxMouseFuncs指向mouse驅動函數,LinuxKeyboardFuncs指向keyboard驅動的函數。
- 在KdInitInput中,建立一個KdMouseInfo對象,放入kdMouseInfo連結清單中,并初始化這個KdMouseInfo對象。
- 在KdInitInput中,把指向keyboard和mouse驅動的指針儲存到kdMouseFuncs和kdKeyboardFuncs兩個全局變量之中。
- 在KdInitInput中,加載鍵盤映射表,初始化按鍵重複的資料結構,重置輸入裝置的狀态。
- 在KdInitInput中,建立keyboard和mouse裝置,并注冊這兩個輸入裝置到系統中。
- 如果支援touchscreen,把kdTsFuncs指向TsFuncs。
keyboard的驅動接口
typedef struct _KdKeyboardFuncs ...{
void (*Load) (void);
int (*Init) (void);
void (*Leds) (int);
void (*Bell) (int, int, int);
void (*Fini) (void);
int LockLed;
} KdKeyboardFuncs;
下面我們來看一個實際例子(tinyx/linux/keyboard.c):
KdKeyboardFuncs LinuxKeyboardFuncs = ...{
LinuxKeyboardLoad,
LinuxKeyboardInit,
LinuxKeyboardLeds,
LinuxKeyboardBell,
LinuxKeyboardFini,
3,
};
LinuxKeyboardLoad: 建構一個鍵值映射表,即從原始的按鍵值到虛拟鍵值間VK之間的映射。讓虛拟鍵值獨立于硬體的,可以提高應用程式的可移植性。
static void
LinuxKeyboardLoad (void)
...{
readKernelMapping ();
}
LinuxKeyboardInit:初始化一些資料結構,并注冊相關回調函數。這樣,在有按鍵事件時,LinuxConsoleFd喚醒select,并調用函數LinuxKeyboardRead讀取按鍵事件,經過一些轉換動作之後,調用KdHandleKeyboardEvent把事件分發出去。
static int
LinuxKeyboardInit (void)
...{
if (!LinuxKbdType)
LinuxKbdType = KdAllocInputType ();
KdRegisterFd (LinuxKbdType, LinuxConsoleFd, LinuxKeyboardRead, 0);
LinuxKeyboardEnable (LinuxConsoleFd, 0);
KdRegisterFdEnableDisable (LinuxConsoleFd,
LinuxKeyboardEnable,
LinuxKeyboardDisable);
return 1;
}
LinuxKeyboardLeds: 在某些鍵按下以後,需要點亮鍵盤燈(Caps Lock和 Num Lock等),這時候會調用這個函數,這是通過ioctl系統調用實作的。
LinuxKeyboardLeds (int leds)
ioctl (LinuxConsoleFd, KDSETLED, leds & 7);
LinuxKeyboardBell:它的功能是發出蜂鳴聲,至于為什麼作為鍵盤驅動的一部分,可能是由于輸入某些鍵值時要響一聲吧(如,/a)。這也是通過ioctl系統調用實作的。
LinuxKeyboardBell (int volume, int pitch, int duration)
if (volume && pitch)
...{
ioctl(LinuxConsoleFd, KDMKTONE,
((1193190 / pitch) & 0xffff) |
(((unsigned long)duration *
volume / 50) << 16));
}
LinuxKeyboardFini:~初始化keyboard裝置,即禁用keyboard,然後登出裝置描述符。
LinuxKeyboardFini (void)
LinuxKeyboardDisable (LinuxConsoleFd, 0);
KdUnregisterFds (LinuxKbdType, FALSE);
滑鼠的接口:
typedef struct _KdMouseFuncs ...{
} KdMouseFuncs;
下面看一個實際例子(tinyx/linux/mouse.c)
KdMouseFuncs LinuxMouseFuncs = ...{
MouseInit,
MouseFini,
MouseInit 打開裝置檔案,并注冊一些回調函數。這樣,在有mouse事件時,fd喚醒select,并調用函數MouseRead讀取mouse事件,經過一些轉換動作之後,調用KdHandleMouseEvent把事件分發出去。
char *kdefaultMouse[] = ...{
"/dev/mouse",
"/dev/psaux",
"/dev/input/mice",
"/dev/adbmouse",
"/dev/ttyS0",
"/dev/ttyS1",
#define NUM_DEFAULT_MOUSE (sizeof (kdefaultMouse) / sizeof (kdefaultMouse[0]))
MouseInit (void)
int i;
int fd = -1;
Kmouse *km;
KdMouseInfo *mi, *next;
int n = 0;
char *prot;
if (!MouseInputType)
MouseInputType = KdAllocInputType ();
for (mi = kdMouseInfo; mi; mi = next)
next = mi->next;
prot = mi->prot;
if (mi->inputType)
continue;
if (!mi->name)
for (i = 0; i < NUM_DEFAULT_MOUSE; i++)
...{
if (kdNoSerialMouse && strstr(kdefaultMouse[i], "/dev/ttyS"))
continue;
fd = open (kdefaultMouse[i], 2);
if (fd >= 0)
mi->name = KdSaveString (kdefaultMouse[i]);
break;
}
}
else
fd = open (mi->name, 2);
if (fd >= 0)
km = (Kmouse *) xalloc (sizeof (Kmouse));
if (km)
km->iob.fd = fd;
km->iob.avail = km->iob.used = 0;
km->prot = 0;
km->i_prot = 0;
km->tty = isatty (fd);
mi->driver = km;
mi->inputType = MouseInputType;
MouseFirstProtocol (km, mi->prot);
if (KdRegisterFd (MouseInputType, fd, MouseRead, (void *) mi))
n++;
else
close (fd);
return n;
MouseFini: ~初始化mouse裝置,即登出回調函數,并釋放一些資料結構。
MouseFini (void)
KdMouseInfo *mi;
KdUnregisterFds (MouseInputType, TRUE);
for (mi = kdMouseInfo; mi; mi = mi->next)
if (mi->inputType == MouseInputType)
xfree (mi->driver);
mi->driver = 0;
mi->inputType = 0;
觸摸屏的接口
觸摸屏是作為滑鼠來實作的。下面看一個實際例子(tinyx/linux/tslib.c)
KdMouseFuncs TsFuncs = ...{
TslibInit,
TslibFini
TslibInit:打開裝置檔案,并注冊一些回調函數。由于觸摸屏涉及到去抖、濾波、校準等功能,相關對于滑鼠來說,要複雜得多,是以這裡是調用tslib來實作的。
在有筆點事件時,fd喚醒select,并調用函數TsRead讀取筆點事件,經過一些轉換動作之後,調用KdHandleMouseEvent把事件分發出去。
int
TslibInit (void)
int i;
int fd= 0;
int n = 0;
if (!TsInputType)
TsInputType = KdAllocInputType ();
next = mi->next;
if (mi->inputType)
if (!mi->name)
for (i = 0; i < NUM_TS_NAMES; i++)
...{
if(!(tsDev = ts_open(TsNames[i],0))) continue;
ts_config(tsDev);
fd=ts_fd(tsDev);
if (fd >= 0)
...{
mi->name = KdSaveString (TsNames[i]);
break;
}
}
if (fd > 0 && tsDev != 0)
...{
mi->driver = (void *) fd;
mi->inputType = TsInputType;
if (KdRegisterFd (TsInputType, fd, TsRead, (void *) mi))
n++;
}
if (fd > 0) close(fd);
TslibFini: ~初始化touchscreen裝置,即登出相關的回調函數,并釋放一些資料結構。
void
TslibFini (void)
KdUnregisterFds (TsInputType, TRUE);
if (mi->inputType == TsInputType)
if(mi->driver) ts_close(tsDev);
mi->driver = 0;
mi->inputType = 0;
if (mi->name != NULL) ...{
free(mi->name);
mi->name = NULL;
}
有意思的是,驅動本身的接口并不能說明裝置的特性,它隻提供初始化和~初始化這類通用接口,在初始化時才注冊裝置描述符和相應的讀取函數。
(待續)