2 * Copyright 2004-2010, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Copyright 2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
19 #include <FindDirectory.h>
21 #include <InterfaceDefs.h>
24 #include <keyboard_mouse_driver.h>
31 struct keyboard
* next
;
39 thread_id console_writer
;
41 struct keyboard
* keyboards
;
49 struct console gConsole
;
53 error(const char* message
, ...)
58 va_start(args
, message
);
60 vsnprintf(buffer
, sizeof(buffer
), message
, args
);
64 // put it out on stderr as well as to serial/syslog
65 fputs(buffer
, stderr
);
66 debug_printf("%s", buffer
);
71 update_leds(int fd
, uint32 modifiers
)
73 char lockIO
[3] = {0, 0, 0};
75 if ((modifiers
& B_NUM_LOCK
) != 0)
77 if ((modifiers
& B_CAPS_LOCK
) != 0)
79 if ((modifiers
& B_SCROLL_LOCK
) != 0)
82 ioctl(fd
, KB_SET_LEDS
, &lockIO
);
87 keyboard_reader(void* arg
)
89 struct keyboard
* keyboard
= (struct keyboard
*)arg
;
90 uint8 activeDeadKey
= 0;
94 // Load current keymap from disk (we can't talk to the input server)
95 // TODO: find a better way (we shouldn't have to care about the on-disk
98 status_t status
= find_directory(B_USER_SETTINGS_DIRECTORY
, -1, false,
100 if (status
== B_OK
) {
101 strlcat(path
, "/Key_map", sizeof(path
));
102 status
= keymap
.SetTo(path
);
105 keymap
.SetToDefault();
108 raw_key_info rawKeyInfo
;
109 if (ioctl(keyboard
->device
, KB_READ
, &rawKeyInfo
) != 0)
112 uint32 keycode
= rawKeyInfo
.keycode
;
113 bool isKeyDown
= rawKeyInfo
.is_keydown
;
118 uint32 changedModifiers
= keymap
.Modifier(keycode
);
119 bool isLock
= (changedModifiers
120 & (B_CAPS_LOCK
| B_NUM_LOCK
| B_SCROLL_LOCK
)) != 0;
121 if (changedModifiers
!= 0 && (!isLock
|| isKeyDown
)) {
122 uint32 oldModifiers
= modifiers
;
124 if ((isKeyDown
&& !isLock
)
125 || (isKeyDown
&& !(modifiers
& changedModifiers
)))
126 modifiers
|= changedModifiers
;
128 modifiers
&= ~changedModifiers
;
130 // ensure that we don't clear a combined B_*_KEY when still
131 // one of the individual B_{LEFT|RIGHT}_*_KEY is pressed
132 if (modifiers
& (B_LEFT_SHIFT_KEY
| B_RIGHT_SHIFT_KEY
))
133 modifiers
|= B_SHIFT_KEY
;
134 if (modifiers
& (B_LEFT_COMMAND_KEY
| B_RIGHT_COMMAND_KEY
))
135 modifiers
|= B_COMMAND_KEY
;
136 if (modifiers
& (B_LEFT_CONTROL_KEY
| B_RIGHT_CONTROL_KEY
))
137 modifiers
|= B_CONTROL_KEY
;
138 if (modifiers
& (B_LEFT_OPTION_KEY
| B_RIGHT_OPTION_KEY
))
139 modifiers
|= B_OPTION_KEY
;
142 if (modifiers
!= oldModifiers
) {
144 update_leds(keyboard
->device
, modifiers
);
148 uint8 newDeadKey
= 0;
149 if (activeDeadKey
== 0 || !isKeyDown
)
150 newDeadKey
= keymap
.ActiveDeadKey(keycode
, modifiers
);
154 if (newDeadKey
== 0 && isKeyDown
) {
155 keymap
.GetChars(keycode
, modifiers
, activeDeadKey
, &string
,
158 write(keyboard
->target
, string
, numBytes
);
163 if (newDeadKey
== 0) {
164 if (isKeyDown
&& !modifiers
&& activeDeadKey
!= 0) {
165 // a dead key was completed
168 } else if (isKeyDown
) {
169 // start of a dead key
170 activeDeadKey
= newDeadKey
;
179 console_writer(void* arg
)
181 struct console
* con
= (struct console
*)arg
;
185 ssize_t length
= read(con
->tty_master_fd
, buffer
, sizeof(buffer
));
189 write(con
->console_fd
, buffer
, length
);
197 stop_keyboards(struct console
* con
)
201 for (struct keyboard
* keyboard
= con
->keyboards
; keyboard
!= NULL
;
202 keyboard
= keyboard
->next
) {
203 close(keyboard
->device
);
206 // wait for the threads
208 for (struct keyboard
* keyboard
= con
->keyboards
; keyboard
!= NULL
;) {
209 struct keyboard
* next
= keyboard
->next
;
210 wait_for_thread(keyboard
->thread
, NULL
);
216 con
->keyboards
= NULL
;
220 /*! Opens the all keyboard drivers it finds starting from the given
221 location \a start that support the debugger extension.
223 static struct keyboard
*
224 open_keyboards(int target
, const char* start
, struct keyboard
* previous
)
226 // Wait for the directory to appear, if we're loaded early in boot
227 // it may take a while for it to appear while the drivers load.
231 dir
= opendir(start
);
239 struct keyboard
* keyboard
= previous
;
242 dirent
* entry
= readdir(dir
);
245 if (!strcmp(entry
->d_name
, ".") || !strcmp(entry
->d_name
, ".."))
249 strlcpy(path
, start
, sizeof(path
));
250 strlcat(path
, "/", sizeof(path
));
251 strlcat(path
, entry
->d_name
, sizeof(path
));
254 if (::stat(path
, &stat
) != 0)
257 if (S_ISDIR(stat
.st_mode
)) {
258 keyboard
= open_keyboards(target
, path
, keyboard
);
262 // Try to open it as a device
263 int fd
= open(path
, O_RDONLY
);
265 // Turn on debugger mode
266 if (ioctl(fd
, KB_SET_DEBUG_READER
, NULL
, 0) == 0) {
267 keyboard
= new ::keyboard();
268 keyboard
->device
= fd
;
269 keyboard
->target
= target
;
270 keyboard
->thread
= spawn_thread(&keyboard_reader
, path
,
271 B_URGENT_DISPLAY_PRIORITY
, keyboard
);
272 if (keyboard
->thread
< 0) {
279 if (previous
!= NULL
)
280 previous
->next
= keyboard
;
282 resume_thread(keyboard
->thread
);
294 start_console(struct console
* con
)
296 memset(con
, 0, sizeof(struct console
));
297 con
->console_fd
= -1;
298 con
->tty_master_fd
= -1;
299 con
->tty_slave_fd
= -1;
300 con
->console_writer
= -1;
302 con
->console_fd
= open("/dev/console", O_WRONLY
);
303 if (con
->console_fd
< 0)
306 DIR* dir
= opendir("/dev/pt");
308 struct dirent
* entry
;
311 while ((entry
= readdir(dir
)) != NULL
) {
312 if (entry
->d_name
[0] == '.')
315 snprintf(name
, sizeof(name
), "/dev/pt/%s", entry
->d_name
);
317 con
->tty_master_fd
= open(name
, O_RDWR
);
318 if (con
->tty_master_fd
>= 0) {
319 snprintf(name
, sizeof(name
), "/dev/tt/%s", entry
->d_name
);
321 con
->tty_slave_fd
= open(name
, O_RDWR
);
322 if (con
->tty_slave_fd
< 0) {
323 error("Could not open tty %s: %s!\n", name
,
325 close(con
->tty_master_fd
);
328 struct termios termios
;
331 if (tcgetattr(con
->tty_slave_fd
, &termios
) == 0) {
332 termios
.c_iflag
= ICRNL
;
333 termios
.c_oflag
= OPOST
| ONLCR
;
334 termios
.c_lflag
= ISIG
| ICANON
| ECHO
| ECHOE
| ECHONL
;
336 tcsetattr(con
->tty_slave_fd
, TCSANOW
, &termios
);
339 if (ioctl(con
->console_fd
, TIOCGWINSZ
, &size
,
340 sizeof(struct winsize
)) == 0) {
341 // we got the window size from the console
342 ioctl(con
->tty_slave_fd
, TIOCSWINSZ
, &size
,
343 sizeof(struct winsize
));
350 setenv("TTY", name
, true);
353 if (con
->tty_master_fd
< 0 || con
->tty_slave_fd
< 0)
357 = open_keyboards(con
->tty_master_fd
, "/dev/input/keyboard", NULL
);
358 if (con
->keyboards
== NULL
)
361 con
->console_writer
= spawn_thread(&console_writer
, "console writer",
362 B_URGENT_DISPLAY_PRIORITY
, con
);
363 if (con
->console_writer
< 0)
366 resume_thread(con
->console_writer
);
367 setenv("TERM", "xterm", true);
374 stop_console(struct console
* con
)
376 // close TTY FDs; this will also unblock the threads
377 close(con
->tty_master_fd
);
378 close(con
->tty_slave_fd
);
380 // close console and keyboards
381 close(con
->console_fd
);
382 wait_for_thread(con
->console_writer
, NULL
);
389 start_process(int argc
, const char** argv
, struct console
* con
)
391 int savedInput
= dup(0);
392 int savedOutput
= dup(1);
393 int savedError
= dup(2);
395 dup2(con
->tty_slave_fd
, 0);
396 dup2(con
->tty_slave_fd
, 1);
397 dup2(con
->tty_slave_fd
, 2);
399 pid_t pid
= load_image(argc
, argv
, (const char**)environ
);
402 tcsetpgrp(con
->tty_slave_fd
, pid
);
405 dup2(savedOutput
, 1);
416 main(int argc
, char** argv
)
418 // we're a session leader
421 int err
= start_console(&gConsole
);
423 error("consoled: error %d starting console.\n", err
);
427 // move our stdin and stdout to the console
428 dup2(gConsole
.tty_slave_fd
, 0);
429 dup2(gConsole
.tty_slave_fd
, 1);
430 dup2(gConsole
.tty_slave_fd
, 2);
433 // a command was given: we run it only once
435 // get the command argument vector
436 int commandArgc
= argc
- 1;
437 const char** commandArgv
= new const char*[commandArgc
+ 1];
438 for (int i
= 0; i
< commandArgc
; i
++)
439 commandArgv
[i
] = argv
[i
+ 1];
441 commandArgv
[commandArgc
] = NULL
;
444 pid_t process
= start_process(commandArgc
, commandArgv
, &gConsole
);
447 wait_for_thread(process
, &returnCode
);
449 // no command given: start a shell in an endless loop
453 const char* shellArgv
[] = { "/bin/sh", "--login", NULL
};
455 shellProcess
= start_process(2, shellArgv
, &gConsole
);
457 wait_for_thread(shellProcess
, &returnCode
);
459 puts("Restart shell");
463 stop_console(&gConsole
);