Event system
Phoenix’s event system is a bit different from CraftOS’s events. Like CraftOS, events are stored in a queue that are pushed to each process when they yield. However, event parameters are now stored in a key-value table instead of unpacked as multiple return values. This makes it easier for developers to quickly grab the parameters they need, and makes code more readable. An event is only sent through two values: the type of the event (a string), and a table with the parameters. For example, a timer event might return "timer", {id = 0}
.
Since threads may yield for reasons other than waiting for events (syscalls, preemption), events are only passed when the thread yields with event
as the first parameter. Each process has its own event queue, and all threads in a process share the event queue. Any thread may wait for an event, and multiple threads may wait at the same time. If multiple threads are waiting for an event, each thread will get the same event when one is passed.
There are a few different types of events that Phoenix may send.
CraftOS events
These events are converted from system events that the computer sends and filtered to the right process(es). They hold the same data that the ComputerCraft events do (potentially with additional metadata added), with names for each parameter.
Here’s the mappings for each event that’s directly exposed to the user. Each parameter column lists the new name of the parameter that is in that position in CraftOS. If an event has a variable number of parameters, the last parameter is a table with the rest of the parameters.
Event type | Parameter 1 | Parameter 2 | Parameter 3 | Parameter 4 | Parameter 5 | Additional parameters |
---|---|---|---|---|---|---|
alarm | id | |||||
char | character | |||||
computer_command | args … | |||||
disk (call hardware.listen first) | device | |||||
disk_eject (call hardware.listen first) | device | |||||
http_failure => handle_status_change | id |
| ||||
http_success => handle_status_change | id |
| ||||
key | keycode | isRepeat |
| |||
key_up | keycode |
| ||||
modem_message (call hardware.listen first) | device | channel | replyChannel | message | distance | |
monitor_resize (call hardware.listen first) | device | |||||
mouse_click | button | x | y |
| ||
mouse_drag | button | x | y |
| ||
mouse_up | button | x | y |
| ||
mouse_scroll | direction | x | y |
| ||
paste | text | |||||
peripheral => device_added (call hardware.listen first) | device | |||||
peripheral_detach => device_removed (call hardware.listen first) | device | |||||
redstone | ||||||
rednet_message => handle_data_ready | id | |||||
speaker_audio_empty | device | |||||
task_complete | id | success | results … | |||
term_resize | ||||||
timer | id | |||||
turtle_inventory | ||||||
websocket_closed => handle_status_changed | id |
| ||||
websocket_failure => handle_status_changed | id |
| ||||
websocket_success => handle_status_changed | id |
| ||||
websocket_message => handle_data_ready | id |
Some events aren’t directly exposed in Phoenix:
http_check
: Thecheckurl
syscall only allows synchronous checking, sohttp_check
is automatically consumed while running and not sent to the program.terminate
:terminate
events are converted toSIGINT
signals, and are not sent as events.
Events related to networking (http_*
, websocket_*
, rednet_message
) are converted to new handle_status_changed
and handle_data_ready
events for use with the networking system. These only send the ID of handles that changed, and the status if changed - use the handle itself to extract any additional data required. Events related to devices are only sent to processes which call the devlisten
syscall (hardware.listen
when using libsystem), and the device
field replaces the side, pointing to the path or UUID of the device the event applies to.
Key event keycodes
Unlike CraftOS, Phoenix exposes a keycode set that is public and consistent, and using hard-coded constants is not discouraged (though it is recommended to use libsystem
’s keys
library for ease of use & reading). Keycodes are automatically converted from CraftOS codes to Phoenix codes in the kernel - it is not necessary to attempt to load the CraftOS keys
API from your program, and in fact those codes will not work at all.
Phoenix’s keycode set uses lowercase (if applicable) ASCII codes for keys with a printable representation, and unprintable keys are organized in groups in the upper byte range. The complete keycode set is listed below, assuming US QWERTY layout.
Complete keycode table
Code | Key name | keys name |
---|---|---|
0x08 | Backspace | keys.backspace |
0x09 | Tab | keys.tab |
0x0A | Enter/return | keys.enter |
0x1B | Escape (reserved) | none |
0x20 | Space Bar | keys.space |
0x27 | Apostrophe/quote | keys.apostrophe |
0x2C | Comma/left angle bracket | keys.comma |
0x2D | Minus/underscore | keys.minus |
0x2E | Period/right angle bracket | keys.period |
0x2F | Slash/question mark | keys.slash |
0x30 | Zero/right parenthesis | keys.zero |
0x31 | One/exclamation mark | keys.one |
0x32 | Two/at symbol | keys.two |
0x33 | Three/hash | keys.three |
0x34 | Four/dollar sign | keys.four |
0x35 | Five/percent sign | keys.five |
0x36 | Six/caret | keys.six |
0x37 | Seven/ampersand | keys.seven |
0x38 | Eight/asterisk | keys.eight |
0x39 | Nine/left parenthesis | keys.nine |
0x3B | Semicolon/colon | keys.semicolon |
0x3D | Equals/plus | keys.equals |
0x5B | Left bracket/left curly brace | keys.leftBracket |
0x5C | Backslash/pipe | keys.backslash |
0x5D | Right bracket/right curly brace | keys.rightBracket |
0x60 | Backtick (grave)/tilde | keys.grave |
0x61 | A | keys.a |
0x62 | B | keys.b |
0x63 | C | keys.c |
0x64 | D | keys.d |
0x65 | E | keys.e |
0x66 | F | keys.f |
0x67 | G | keys.g |
0x68 | H | keys.h |
0x69 | I | keys.i |
0x6A | J | keys.j |
0x6B | K | keys.k |
0x6C | L | keys.l |
0x6D | M | keys.m |
0x6E | N | keys.n |
0x6F | O | keys.o |
0x70 | P | keys.p |
0x71 | Q | keys.q |
0x72 | R | keys.r |
0x73 | S | keys.s |
0x74 | T | keys.t |
0x75 | U | keys.u |
0x76 | V | keys.v |
0x77 | W | keys.w |
0x78 | X | keys.x |
0x79 | Y | keys.y |
0x7A | Z | keys.z |
0x7F | Delete | keys.delete |
0x80 | Insert | keys.insert |
0x81 | F1 | keys.f1 |
0x82 | F2 | keys.f2 |
0x83 | F3 | keys.f3 |
0x84 | F4 | keys.f4 |
0x85 | F5 | keys.f5 |
0x86 | F6 | keys.f6 |
0x87 | F7 | keys.f7 |
0x88 | F8 | keys.f8 |
0x89 | F9 | keys.f9 |
0x8A | F10 | keys.f10 |
0x8B | F11 | keys.f11 |
0x8C | F12 | keys.f12 |
0x8D | F13 | keys.f13 |
0x8E | F14 | keys.f14 |
0x8F | F15 | keys.f15 |
0x90 | F16 | keys.f16 |
0x91 | F17 | keys.f17 |
0x92 | F18 | keys.f18 |
0x93 | F19 | keys.f19 |
0x94 | F20 | keys.f20 |
0x95 | F21 | keys.f21 |
0x96 | F22 | keys.f22 |
0x97 | F23 | keys.f23 |
0x98 | F24 | keys.f24 |
0x99 | F25 | keys.f25 |
0x9A | Convert (Japanese) | keys.convert |
0x9B | No Convert (Japanese) | keys.noconvert |
0x9C | Kana (Japanese) | keys.kana |
0x9D | Kanji (Japanese) | keys.kanji |
0x9E | Yen | keys.yen |
0x9F | Num Pad Decimal | keys.numPadDecimal |
0xA0 | Num Pad 0 | keys.numPad0 |
0xA1 | Num Pad 1 | keys.numPad1 |
0xA2 | Num Pad 2 | keys.numPad2 |
0xA3 | Num Pad 3 | keys.numPad3 |
0xA4 | Num Pad 4 | keys.numPad4 |
0xA5 | Num Pad 5 | keys.numPad5 |
0xA6 | Num Pad 6 | keys.numPad6 |
0xA7 | Num Pad 7 | keys.numPad7 |
0xA8 | Num Pad 8 | keys.numPad8 |
0xA9 | Num Pad 9 | keys.numPad9 |
0xAA | Num Pad Add | keys.numPadAdd |
0xAB | Num Pad Subtract | keys.numPadSubtract |
0xAC | Num Pad Multiply | keys.numPadMultiply |
0xAD | Num Pad Divide | keys.numPadDivide |
0xAE | Num Pad Equals | keys.numPadEquals |
0xAF | Num Pad Enter | keys.numPadEnter |
0xB0 | Left Control | keys.leftCtrl |
0xB1 | Right Control | keys.rightCtrl |
0xB2 | Left Alt (Option) | keys.leftAlt |
0xB3 | Right Alt (Option) | keys.rightAlt |
0xB4 | Left Shift | keys.leftShift |
0xB5 | Right Shift | keys.rightShift |
0xB6 | Left Super (Windows/Command) | keys.leftSuper |
0xB7 | Right Super (Windows/Command) | keys.rightSuper |
0xB8 | Caps Lock | keys.capsLock |
0xB9 | Num Lock | keys.numLock |
0xBA | Scroll Lock | keys.scrollLock |
0xBB | Print Screen | keys.printScreen |
0xBC | Pause | keys.pause |
0xBD | Menu | keys.menu |
0xBE | Stop | keys.stop |
0xBF | Ax | keys.ax |
0xC0 | Up | keys.up |
0xC1 | Down | keys.down |
0xC2 | Left | keys.left |
0xC3 | Right | keys.right |
0xC4 | Page Up | keys.pageUp |
0xC5 | Page Down | keys.pageDown |
0xC6 | Home | keys.home |
0xC7 | End | keys.end |
0xC8 | Circumflex | keys.circumflex |
0xC9 | At | keys.at |
0xCA | Colon | keys.colon |
0xCB | Underscore | keys.underscore |
Phoenix events
These events are generated by various parts of the kernel, and are not directly associated with any underlying CraftOS event. They are sent through coroutine.yield()
like any other event. This list also includes events that can be triggered by both CraftOS events and Phoenix routines.
Event type | Description | Parameters |
---|---|---|
device_added | Sent when a device is added as a child of a device with events enabled. |
|
device_removed | Sent when a device’s child is removed. |
|
fsevent | Sent when a process calls fsevent on a path, and that path is modified. |
|
handle_data_ready | Sent when a connected network handle receives more data. |
|
handle_status_change | Sent when the status of a network handle changes. |
|
network_event | Sent when a control network message is received after a netevent call. | A copy of the control message |
network_request | Sent when a listening PSP socket receives a new connection. |
|
process_complete | Sent when a child process exits. |
|
remote_event | Sent when another process posts a remote event, explained below. | See below |
syslog | Sent when a message is posted to an opened log. | A copy of the options table passed to the corresponding syslog call |
syslog_close | Sent when an opened log is removed from the system. |
|
tty_event | Sent when a TTY created with mktty has its contents updated. |
|
Filesystem event types
| Event | File? | Dir? | Description | |———————|——-|——-|————-| | open
| x | | Triggered when the target file is opened in write or append mode. | | open_child
| | x | Triggered when the target directory has a file opened in write or append mode. | | remove
| x | x | Triggered when the target file or directory is removed. | | remove_child
| | x | Triggered when the target directory has a child removed. | | rename_from
| x | x | Triggered when the target file or directory is being renamed. | | rename_from_child
| | x | Triggered when the target directory has a child renamed. | | rename_to
| | | Triggered when the target path has been replaced with a renamed file or directory. | | rename_to_child
| | x | Triggered when the target directory has a new child from a rename. | | mkdir
| | | Triggered when the target path now exists and is a directory. | | mkdir_child
| | x | Triggered when the target directory has a new child directory. | | link
| | | Triggered when the target path now exists and is a link. | | link_child
| | x | Triggered when the target directory has a new child link. | | mkfifo
| | | Triggered when the target path now exists and is a FIFO. | | mkfifo_child
| | x | Triggered when the target directory has a new child FIFO. |
Signals
Signals are the most basic type of IPC in Phoenix. They do not have any metadata associated with them, and are only used to send a trigger to a process that will be executed immediately. The event type is "signal"
, and the parameters contains a single field called signal
with the signal sent. A signal is sent to a process through the kill
syscall, taking the PID of the target process and the signal to send. A signal may be any number, but the system only uses the signals listed below. Signals may only be sent to processes running as the same user as the sender, unless the sender is running as root.
For most signals, they are simply sent as events to the process. However, processes can also have a function called whenever a signal is sent to the process. This will cause the signal to not be added to the event queue. Many of the built-in signals have handlers attached by default - notably, the ones that kill the process. Processes can replace or remove these handlers (or handlers for other signals) through the signal
syscall.
In the case of SIGABRT
, an additional parameter is added to the signal: error
. This parameter is passed when an uncaught error occurs, and is also passed to the handler function if one is installed. This allows the process to add a global error handler to the process that can transform the error message as needed. By default, this simply calls debug.traceback
on the message and prints it to the system log, as well as sending the message to standard error.
ID | POSIX name | libsystem name | Built-in triggers | Default action |
---|---|---|---|---|
1 | SIGHUP | hangup | Terminal disconnected | Terminate |
2 | SIGINT | interrupt | ^C is pressed | Terminate |
3 | SIGQUIT | quit | ^\ is pressed | Terminate w/traceback |
6 | SIGABRT | abort | Uncaught error occurs | Terminate w/traceback |
9 | SIGKILL | kill | Kill (cannot be overridden) | |
10 | SIGUSR1 | user1 | Ignore | |
12 | SIGUSR2 | user2 | Ignore | |
13 | SIGPIPE | pipe | Pipe breaks | Terminate |
15 | SIGTERM | terminate | Terminate | |
18 | SIGCONT | continue | Process continued | Ignore |
19 | SIGSTOP | stop | ^Z is pressed | Stop |
21 | SIGTTIN | bg_input | Input from background | Stop |
22 | SIGTTOU | bg_output | Output from background | Stop |
Remote events
Remote events are the primary method of IPC in Phoenix. A remote event is sent with the type "remote_event"
, and can be sent from any process to any process. A remote event may contain any data as the payload, as well as an independent type. The parameter table contains the following fields:
sender
: The PID of the process that sent the event.type
: The type of event as reported by the sender.data
: The parameter sent to the process.