Compare commits
2 Commits
e4ce686576
...
917722cf70
Author | SHA1 | Date |
---|---|---|
Jonas 'Sortie' Termansen | 917722cf70 | |
Jonas 'Sortie' Termansen | b384bce28c |
5
Makefile
5
Makefile
|
@ -6,8 +6,9 @@ include build-aux/version.mak
|
||||||
MODULES=\
|
MODULES=\
|
||||||
libc \
|
libc \
|
||||||
libm \
|
libm \
|
||||||
dispd \
|
libdisplay \
|
||||||
libmount \
|
libmount \
|
||||||
|
libui \
|
||||||
bench \
|
bench \
|
||||||
carray \
|
carray \
|
||||||
checksum \
|
checksum \
|
||||||
|
@ -15,6 +16,7 @@ chkblayout \
|
||||||
chvideomode \
|
chvideomode \
|
||||||
dhclient \
|
dhclient \
|
||||||
disked \
|
disked \
|
||||||
|
display \
|
||||||
dnsconfig \
|
dnsconfig \
|
||||||
editor \
|
editor \
|
||||||
ext \
|
ext \
|
||||||
|
@ -32,6 +34,7 @@ rw \
|
||||||
sf \
|
sf \
|
||||||
sh \
|
sh \
|
||||||
sysinstall \
|
sysinstall \
|
||||||
|
terminal \
|
||||||
tix \
|
tix \
|
||||||
trianglix \
|
trianglix \
|
||||||
update-initrd \
|
update-initrd \
|
||||||
|
|
|
@ -198,6 +198,7 @@ fi
|
||||||
set enable_src=true
|
set enable_src=true
|
||||||
set enable_network_drivers=
|
set enable_network_drivers=
|
||||||
set enable_dhclient=true
|
set enable_dhclient=true
|
||||||
|
set enable_gui=true
|
||||||
set enable_ntpd=false
|
set enable_ntpd=false
|
||||||
set enable_sshd=false
|
set enable_sshd=false
|
||||||
|
|
||||||
|
@ -214,6 +215,7 @@ export no_random_seed
|
||||||
export enable_src
|
export enable_src
|
||||||
export enable_network_drivers
|
export enable_network_drivers
|
||||||
export enable_dhclient
|
export enable_dhclient
|
||||||
|
export enable_gui
|
||||||
export enable_ntpd
|
export enable_ntpd
|
||||||
export enable_sshd
|
export enable_sshd
|
||||||
EOF
|
EOF
|
||||||
|
@ -402,10 +404,11 @@ menuentry() {
|
||||||
echo
|
echo
|
||||||
printf "menuentry \"Sortix (%s)\" {\n" "$1"
|
printf "menuentry \"Sortix (%s)\" {\n" "$1"
|
||||||
if [ -n "$2" ]; then
|
if [ -n "$2" ]; then
|
||||||
printf " load_sortix %s\n" "$2"
|
printf " if \$enable_gui; then\n"
|
||||||
#printf " load_sortix '"
|
printf " load_sortix %s-gui\n" "$2"
|
||||||
#printf '%s' "$2" | sed "s,','\\'',g"
|
printf " else\n"
|
||||||
#printf "'\n"
|
printf " load_sortix %s\n" "$2"
|
||||||
|
printf " fi\n"
|
||||||
else
|
else
|
||||||
printf " load_sortix\n"
|
printf " load_sortix\n"
|
||||||
fi
|
fi
|
||||||
|
@ -418,7 +421,7 @@ menu_title="\$base_menu_title"
|
||||||
hook_menu_pre
|
hook_menu_pre
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
menuentry "\$title_single_user" '-- /sbin/init'
|
menuentry "\$title_single_user" '-- /sbin/init --target=single-user'
|
||||||
menuentry "\$title_sysinstall" '-- /sbin/init --target=sysinstall'
|
menuentry "\$title_sysinstall" '-- /sbin/init --target=sysinstall'
|
||||||
menuentry "\$title_sysupgrade" '-- /sbin/init --target=sysupgrade'
|
menuentry "\$title_sysupgrade" '-- /sbin/init --target=sysupgrade'
|
||||||
|
|
||||||
|
@ -446,6 +449,18 @@ menu_title="\$base_menu_title - Advanced Options"
|
||||||
|
|
||||||
hook_advanced_menu_pre
|
hook_advanced_menu_pre
|
||||||
|
|
||||||
|
if "\$enable_gui"; then
|
||||||
|
menuentry "Disable GUI" {
|
||||||
|
enable_gui=false
|
||||||
|
configfile /boot/grub/advanced.cfg
|
||||||
|
}
|
||||||
|
else
|
||||||
|
menuentry "Enable GUI" {
|
||||||
|
enable_gui=true
|
||||||
|
configfile /boot/grub/advanced.cfg
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
if "\$enable_src"; then
|
if "\$enable_src"; then
|
||||||
menuentry "Disable loading source code" {
|
menuentry "Disable loading source code" {
|
||||||
enable_src=false
|
enable_src=false
|
||||||
|
|
|
@ -151,6 +151,7 @@ EOF
|
||||||
tix-iso-bootconfig \
|
tix-iso-bootconfig \
|
||||||
--random-seed \
|
--random-seed \
|
||||||
--timeout=0 \
|
--timeout=0 \
|
||||||
|
--disable-gui \
|
||||||
--liveconfig=liveconfig \
|
--liveconfig=liveconfig \
|
||||||
bootconfig
|
bootconfig
|
||||||
mkdir -p bootconfig/boot/grub
|
mkdir -p bootconfig/boot/grub
|
||||||
|
|
|
@ -11,7 +11,7 @@ CFLAGS += -Wall -Wextra
|
||||||
BINARIES = chkblayout
|
BINARIES = chkblayout
|
||||||
MANPAGES1 = chkblayout.1
|
MANPAGES1 = chkblayout.1
|
||||||
|
|
||||||
LIBS =
|
LIBS = -ldisplay
|
||||||
|
|
||||||
all: $(BINARIES)
|
all: $(BINARIES)
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <display.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <error.h>
|
#include <error.h>
|
||||||
|
@ -34,6 +35,20 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
|
#define CHKBLAYOUT_ID 0
|
||||||
|
|
||||||
|
static bool chkblayout_ack_received = false;
|
||||||
|
static int chkblayout_error;
|
||||||
|
|
||||||
|
static void on_ack(void* ctx, uint32_t id, int32_t error)
|
||||||
|
{
|
||||||
|
(void) ctx;
|
||||||
|
if ( id != CHKBLAYOUT_ID )
|
||||||
|
return;
|
||||||
|
chkblayout_error = error;
|
||||||
|
chkblayout_ack_received = true;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
bool list = false;
|
bool list = false;
|
||||||
|
@ -103,8 +118,29 @@ int main(int argc, char* argv[])
|
||||||
err(1, "read: %s", kblayout_path);
|
err(1, "read: %s", kblayout_path);
|
||||||
close(kblayout_fd);
|
close(kblayout_fd);
|
||||||
|
|
||||||
if ( tcsetblob(tty_fd, "kblayout", kblayout, kblayout_size) < 0 )
|
if ( getenv("DISPLAY_SOCKET") )
|
||||||
err(1, "tcsetblob: kblayout: %s:", kblayout_path);
|
{
|
||||||
|
struct display_connection* connection = display_connect_default();
|
||||||
|
if ( !connection )
|
||||||
|
err(1, "Could not connect to display server");
|
||||||
|
|
||||||
|
display_chkblayout(connection, CHKBLAYOUT_ID, kblayout, kblayout_size);
|
||||||
|
|
||||||
|
struct display_event_handlers handlers = {0};
|
||||||
|
handlers.ack_handler = on_ack;
|
||||||
|
while ( !chkblayout_ack_received )
|
||||||
|
display_wait_event(connection, &handlers);
|
||||||
|
|
||||||
|
if ( chkblayout_error )
|
||||||
|
{
|
||||||
|
errno = chkblayout_error;
|
||||||
|
err(1, "tcsetblob: kblayout: %s", kblayout_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
display_disconnect(connection);
|
||||||
|
}
|
||||||
|
else if ( tcsetblob(tty_fd, "kblayout", kblayout, kblayout_size) < 0 )
|
||||||
|
err(1, "tcsetblob: kblayout: %s", kblayout_path);
|
||||||
|
|
||||||
free(kblayout);
|
free(kblayout);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ CFLAGS += -Wall -Wextra
|
||||||
BINARIES = chvideomode
|
BINARIES = chvideomode
|
||||||
MANPAGES1 = chvideomode.1
|
MANPAGES1 = chvideomode.1
|
||||||
|
|
||||||
LIBS =
|
LIBS = -ldisplay
|
||||||
|
|
||||||
all: $(BINARIES)
|
all: $(BINARIES)
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <display.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -37,7 +39,22 @@
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
struct termios saved;
|
#define REQUEST_DISPLAYS_ID 0
|
||||||
|
#define REQUEST_DISPLAY_MODES_ID 1
|
||||||
|
#define SET_DISPLAY_MODE_ID 2
|
||||||
|
|
||||||
|
static uint32_t display_id;
|
||||||
|
static bool displays_received = false;
|
||||||
|
|
||||||
|
static size_t modes_count = 0;
|
||||||
|
static struct dispmsg_crtc_mode* modes;
|
||||||
|
static int request_display_modes_error = 0;
|
||||||
|
static bool modes_received = false;
|
||||||
|
|
||||||
|
static int set_display_mode_error = 0;
|
||||||
|
static bool set_display_mode_ack_received;
|
||||||
|
|
||||||
|
static struct termios saved;
|
||||||
|
|
||||||
static void restore_terminal(int sig)
|
static void restore_terminal(int sig)
|
||||||
{
|
{
|
||||||
|
@ -48,6 +65,88 @@ static void restore_terminal(int sig)
|
||||||
raise(sig);
|
raise(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void on_displays(void* ctx, uint32_t id, uint32_t displays)
|
||||||
|
{
|
||||||
|
(void) ctx;
|
||||||
|
if ( id != REQUEST_DISPLAYS_ID )
|
||||||
|
return;
|
||||||
|
if ( displays < 1 )
|
||||||
|
errx(1, "No displays available");
|
||||||
|
display_id = 0; // TODO: Multimonitor support.
|
||||||
|
displays_received = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_display_modes(void* ctx, uint32_t id,
|
||||||
|
uint32_t display_modes_count,
|
||||||
|
void* aux, size_t aux_size)
|
||||||
|
{
|
||||||
|
(void) ctx;
|
||||||
|
assert(display_modes_count * sizeof(struct dispmsg_crtc_mode) == aux_size);
|
||||||
|
if ( id != REQUEST_DISPLAY_MODES_ID )
|
||||||
|
return;
|
||||||
|
modes = malloc(aux_size);
|
||||||
|
if ( !modes )
|
||||||
|
err(1, "malloc");
|
||||||
|
memcpy(modes, aux, aux_size);
|
||||||
|
modes_count = display_modes_count;
|
||||||
|
modes_received = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_ack(void* ctx, uint32_t id, int32_t error)
|
||||||
|
{
|
||||||
|
(void) ctx;
|
||||||
|
switch ( id )
|
||||||
|
{
|
||||||
|
case REQUEST_DISPLAY_MODES_ID:
|
||||||
|
if ( error )
|
||||||
|
{
|
||||||
|
modes = NULL;
|
||||||
|
request_display_modes_error = error;
|
||||||
|
modes_received = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SET_DISPLAY_MODE_ID:
|
||||||
|
set_display_mode_error = error;
|
||||||
|
set_display_mode_ack_received = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void request_displays(struct display_connection* connection)
|
||||||
|
{
|
||||||
|
display_request_displays(connection, REQUEST_DISPLAYS_ID);
|
||||||
|
struct display_event_handlers handlers = {0};
|
||||||
|
handlers.displays_handler = on_displays;
|
||||||
|
while ( !displays_received )
|
||||||
|
display_wait_event(connection, &handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void request_display_modes(struct display_connection* connection,
|
||||||
|
uint32_t display_id)
|
||||||
|
{
|
||||||
|
display_request_display_modes(connection, REQUEST_DISPLAY_MODES_ID,
|
||||||
|
display_id);
|
||||||
|
struct display_event_handlers handlers = {0};
|
||||||
|
handlers.display_modes_handler = on_display_modes;
|
||||||
|
handlers.ack_handler = on_ack;
|
||||||
|
while ( !modes_received )
|
||||||
|
display_wait_event(connection, &handlers);
|
||||||
|
errno = request_display_modes_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool request_set_display_mode(struct display_connection* connection,
|
||||||
|
uint32_t display_id,
|
||||||
|
struct dispmsg_crtc_mode mode)
|
||||||
|
{
|
||||||
|
display_set_display_mode(connection, SET_DISPLAY_MODE_ID, display_id, mode);
|
||||||
|
struct display_event_handlers handlers = {0};
|
||||||
|
handlers.ack_handler = on_ack;
|
||||||
|
set_display_mode_ack_received = false;
|
||||||
|
while ( !set_display_mode_ack_received )
|
||||||
|
display_wait_event(connection, &handlers);
|
||||||
|
return !(errno = set_display_mode_error);
|
||||||
|
}
|
||||||
|
|
||||||
static bool set_current_mode(const struct tiocgdisplay* display,
|
static bool set_current_mode(const struct tiocgdisplay* display,
|
||||||
struct dispmsg_crtc_mode mode)
|
struct dispmsg_crtc_mode mode)
|
||||||
{
|
{
|
||||||
|
@ -61,7 +160,7 @@ static bool set_current_mode(const struct tiocgdisplay* display,
|
||||||
|
|
||||||
static struct dispmsg_crtc_mode*
|
static struct dispmsg_crtc_mode*
|
||||||
get_available_modes(const struct tiocgdisplay* display,
|
get_available_modes(const struct tiocgdisplay* display,
|
||||||
size_t* num_modes_ptr)
|
size_t* modes_count_ptr)
|
||||||
{
|
{
|
||||||
struct dispmsg_get_crtc_modes msg;
|
struct dispmsg_get_crtc_modes msg;
|
||||||
msg.msgid = DISPMSG_GET_CRTC_MODES;
|
msg.msgid = DISPMSG_GET_CRTC_MODES;
|
||||||
|
@ -70,15 +169,15 @@ get_available_modes(const struct tiocgdisplay* display,
|
||||||
size_t guess = 1;
|
size_t guess = 1;
|
||||||
while ( true )
|
while ( true )
|
||||||
{
|
{
|
||||||
struct dispmsg_crtc_mode* ret = (struct dispmsg_crtc_mode*)
|
struct dispmsg_crtc_mode* ret =
|
||||||
malloc(sizeof(struct dispmsg_crtc_mode) * guess);
|
calloc(guess, sizeof(struct dispmsg_crtc_mode));
|
||||||
if ( !ret )
|
if ( !ret )
|
||||||
return NULL;
|
return NULL;
|
||||||
msg.modes_length = guess;
|
msg.modes_length = guess;
|
||||||
msg.modes = ret;
|
msg.modes = ret;
|
||||||
if ( dispmsg_issue(&msg, sizeof(msg)) == 0 )
|
if ( dispmsg_issue(&msg, sizeof(msg)) == 0 )
|
||||||
{
|
{
|
||||||
*num_modes_ptr = guess;
|
*modes_count_ptr = guess;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
free(ret);
|
free(ret);
|
||||||
|
@ -144,21 +243,21 @@ static bool mode_passes_filter(struct dispmsg_crtc_mode mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void filter_modes(struct dispmsg_crtc_mode* modes,
|
static void filter_modes(struct dispmsg_crtc_mode* modes,
|
||||||
size_t* num_modes_ptr,
|
size_t* modes_count_ptr,
|
||||||
struct filter* filter)
|
struct filter* filter)
|
||||||
{
|
{
|
||||||
size_t in_num = *num_modes_ptr;
|
size_t in_count = *modes_count_ptr;
|
||||||
size_t out_num = 0;
|
size_t out_count = 0;
|
||||||
for ( size_t i = 0; i < in_num; i++ )
|
for ( size_t i = 0; i < in_count; i++ )
|
||||||
{
|
{
|
||||||
if ( mode_passes_filter(modes[i], filter) )
|
if ( mode_passes_filter(modes[i], filter) )
|
||||||
modes[out_num++] = modes[i];
|
modes[out_count++] = modes[i];
|
||||||
}
|
}
|
||||||
*num_modes_ptr = out_num;
|
*modes_count_ptr = out_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool get_mode(struct dispmsg_crtc_mode* modes,
|
static bool get_mode(struct dispmsg_crtc_mode* modes,
|
||||||
size_t num_modes,
|
size_t modes_count,
|
||||||
unsigned int xres,
|
unsigned int xres,
|
||||||
unsigned int yres,
|
unsigned int yres,
|
||||||
unsigned int bpp,
|
unsigned int bpp,
|
||||||
|
@ -168,7 +267,7 @@ static bool get_mode(struct dispmsg_crtc_mode* modes,
|
||||||
bool found_other = false;
|
bool found_other = false;
|
||||||
size_t index;
|
size_t index;
|
||||||
size_t other_index = 0;
|
size_t other_index = 0;
|
||||||
for ( size_t i = 0; i < num_modes; i++ )
|
for ( size_t i = 0; i < modes_count; i++ )
|
||||||
{
|
{
|
||||||
if ( modes[i].view_xres == xres &&
|
if ( modes[i].view_xres == xres &&
|
||||||
modes[i].view_yres == yres &&
|
modes[i].view_yres == yres &&
|
||||||
|
@ -208,16 +307,16 @@ static bool get_mode(struct dispmsg_crtc_mode* modes,
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool select_mode(struct dispmsg_crtc_mode* modes,
|
static bool select_mode(struct dispmsg_crtc_mode* modes,
|
||||||
size_t num_modes,
|
size_t modes_count,
|
||||||
int mode_set_error,
|
int mode_set_error,
|
||||||
struct dispmsg_crtc_mode* mode)
|
struct dispmsg_crtc_mode* mode)
|
||||||
{
|
{
|
||||||
if ( !isatty(0) )
|
if ( !isatty(0) )
|
||||||
errx(1, "Interactive menu requires stdin to be a terminal");
|
errx(1, "Interactive menu requires stdin to be a terminal");
|
||||||
|
|
||||||
int num_modes_display_length = 1;
|
int modes_count_display_length = 1;
|
||||||
for ( size_t i = num_modes; 10 <= i; i /= 10 )
|
for ( size_t i = modes_count; 10 <= i; i /= 10 )
|
||||||
num_modes_display_length++;
|
modes_count_display_length++;
|
||||||
|
|
||||||
size_t selection = 0;
|
size_t selection = 0;
|
||||||
bool decided = false;
|
bool decided = false;
|
||||||
|
@ -239,7 +338,7 @@ static bool select_mode(struct dispmsg_crtc_mode* modes,
|
||||||
size_t entries_per_page = ws.ws_row - off;
|
size_t entries_per_page = ws.ws_row - off;
|
||||||
size_t page = selection / entries_per_page;
|
size_t page = selection / entries_per_page;
|
||||||
size_t from = page * entries_per_page;
|
size_t from = page * entries_per_page;
|
||||||
size_t how_many_available = num_modes - from;
|
size_t how_many_available = modes_count - from;
|
||||||
size_t how_many = entries_per_page;
|
size_t how_many = entries_per_page;
|
||||||
if ( how_many_available < how_many )
|
if ( how_many_available < how_many )
|
||||||
how_many = how_many_available;
|
how_many = how_many_available;
|
||||||
|
@ -259,7 +358,7 @@ static bool select_mode(struct dispmsg_crtc_mode* modes,
|
||||||
const char* color = index == selection ? "\e[31m" : "\e[m";
|
const char* color = index == selection ? "\e[31m" : "\e[m";
|
||||||
printf("%s", color);
|
printf("%s", color);
|
||||||
printf("\e[2K");
|
printf("\e[2K");
|
||||||
printf(" [%-*zu] ", num_modes_display_length, index);
|
printf(" [%-*zu] ", modes_count_display_length, index);
|
||||||
if ( modes[index].control & DISPMSG_CONTROL_VALID )
|
if ( modes[index].control & DISPMSG_CONTROL_VALID )
|
||||||
printf("%ux%ux%u",
|
printf("%ux%ux%u",
|
||||||
modes[index].view_xres,
|
modes[index].view_xres,
|
||||||
|
@ -325,12 +424,12 @@ static bool select_mode(struct dispmsg_crtc_mode* modes,
|
||||||
if ( selection )
|
if ( selection )
|
||||||
selection--;
|
selection--;
|
||||||
else
|
else
|
||||||
selection = num_modes - 1;
|
selection = modes_count - 1;
|
||||||
redraw = true;
|
redraw = true;
|
||||||
}
|
}
|
||||||
else if ( length == 1 && byte == 'B' ) // Down key
|
else if ( length == 1 && byte == 'B' ) // Down key
|
||||||
{
|
{
|
||||||
if ( selection + 1 == num_modes )
|
if ( selection + 1 == modes_count )
|
||||||
selection = 0;
|
selection = 0;
|
||||||
else
|
else
|
||||||
selection++;
|
selection++;
|
||||||
|
@ -343,7 +442,7 @@ static bool select_mode(struct dispmsg_crtc_mode* modes,
|
||||||
else if ( '0' <= byte && byte <= '9' )
|
else if ( '0' <= byte && byte <= '9' )
|
||||||
{
|
{
|
||||||
uint32_t requested = byte - '0';
|
uint32_t requested = byte - '0';
|
||||||
if ( requested < num_modes )
|
if ( requested < modes_count )
|
||||||
{
|
{
|
||||||
selection = requested;
|
selection = requested;
|
||||||
redraw = true;
|
redraw = true;
|
||||||
|
@ -510,22 +609,37 @@ int main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool use_display = getenv("DISPLAY_SOCKET");
|
||||||
|
|
||||||
|
struct display_connection* connection = NULL;
|
||||||
struct tiocgdisplay display;
|
struct tiocgdisplay display;
|
||||||
struct tiocgdisplays gdisplays = {0};
|
if ( use_display )
|
||||||
gdisplays.count = 1;
|
|
||||||
gdisplays.displays = &display;
|
|
||||||
if ( ioctl(1, TIOCGDISPLAYS, &gdisplays) < 0 || gdisplays.count == 0 )
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "No video devices are associated with this terminal.\n");
|
connection = display_connect_default();
|
||||||
exit(13);
|
if ( !connection )
|
||||||
|
err(1, "Could not connect to display server");
|
||||||
|
request_displays(connection);
|
||||||
|
request_display_modes(connection, display_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct tiocgdisplays gdisplays = {0};
|
||||||
|
// TODO: Multimonitor support.
|
||||||
|
gdisplays.count = 1;
|
||||||
|
gdisplays.displays = &display;
|
||||||
|
if ( ioctl(1, TIOCGDISPLAYS, &gdisplays) < 0 || gdisplays.count == 0 )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "No displays associated with this terminal.\n");
|
||||||
|
exit(13);
|
||||||
|
}
|
||||||
|
|
||||||
|
modes = get_available_modes(&display, &modes_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t num_modes = 0;
|
|
||||||
struct dispmsg_crtc_mode* modes = get_available_modes(&display, &num_modes);
|
|
||||||
if ( !modes )
|
if ( !modes )
|
||||||
err(1, "Unable to detect available video modes");
|
err(1, "Unable to detect available video modes");
|
||||||
|
|
||||||
if ( !num_modes )
|
if ( !modes_count )
|
||||||
{
|
{
|
||||||
fprintf(stderr, "No video modes are currently available.\n");
|
fprintf(stderr, "No video modes are currently available.\n");
|
||||||
fprintf(stderr, "Try make sure a device driver exists and is "
|
fprintf(stderr, "Try make sure a device driver exists and is "
|
||||||
|
@ -533,8 +647,8 @@ int main(int argc, char* argv[])
|
||||||
exit(11);
|
exit(11);
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_modes(modes, &num_modes, &filter);
|
filter_modes(modes, &modes_count, &filter);
|
||||||
if ( !num_modes )
|
if ( !modes_count )
|
||||||
{
|
{
|
||||||
fprintf(stderr, "No video mode remains after filtering away unwanted "
|
fprintf(stderr, "No video mode remains after filtering away unwanted "
|
||||||
"modes.\n");
|
"modes.\n");
|
||||||
|
@ -552,10 +666,15 @@ int main(int argc, char* argv[])
|
||||||
errx(1, "Invalid video mode: %s", argv[optind]);
|
errx(1, "Invalid video mode: %s", argv[optind]);
|
||||||
|
|
||||||
struct dispmsg_crtc_mode mode;
|
struct dispmsg_crtc_mode mode;
|
||||||
if ( !get_mode(modes, num_modes, xres, yres, bpp, &mode) )
|
if ( !get_mode(modes, modes_count, xres, yres, bpp, &mode) )
|
||||||
errx(1, "No such available resolution: %s", argv[optind]);
|
errx(1, "No such available resolution: %s", argv[optind]);
|
||||||
|
|
||||||
if ( !set_current_mode(&display, mode) )
|
bool mode_set;
|
||||||
|
if ( use_display )
|
||||||
|
mode_set = request_set_display_mode(connection, display_id, mode);
|
||||||
|
else
|
||||||
|
mode_set = set_current_mode(&display, mode);
|
||||||
|
if ( !mode_set )
|
||||||
err(1, "Failed to set video mode %jux%jux%ju",
|
err(1, "Failed to set video mode %jux%jux%ju",
|
||||||
(uintmax_t) mode.view_xres,
|
(uintmax_t) mode.view_xres,
|
||||||
(uintmax_t) mode.view_yres,
|
(uintmax_t) mode.view_yres,
|
||||||
|
@ -568,10 +687,15 @@ int main(int argc, char* argv[])
|
||||||
while ( !mode_set )
|
while ( !mode_set )
|
||||||
{
|
{
|
||||||
struct dispmsg_crtc_mode mode;
|
struct dispmsg_crtc_mode mode;
|
||||||
if ( !select_mode(modes, num_modes, mode_set_error, &mode) )
|
if ( !select_mode(modes, modes_count, mode_set_error, &mode) )
|
||||||
exit(10);
|
exit(10);
|
||||||
|
|
||||||
if ( !(mode_set = set_current_mode(&display, mode)) )
|
if ( use_display )
|
||||||
|
mode_set = request_set_display_mode(connection, display_id,
|
||||||
|
mode);
|
||||||
|
else
|
||||||
|
mode_set = set_current_mode(&display, mode);
|
||||||
|
if ( !mode_set )
|
||||||
{
|
{
|
||||||
mode_set_error = errno;
|
mode_set_error = errno;
|
||||||
warn("Failed to set video mode %jux%jux%ju",
|
warn("Failed to set video mode %jux%jux%ju",
|
||||||
|
@ -582,5 +706,8 @@ int main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( use_display )
|
||||||
|
display_disconnect(connection);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
*.a
|
|
||||||
*.o
|
|
||||||
server/dispd
|
|
|
@ -1,53 +0,0 @@
|
||||||
SOFTWARE_MEANT_FOR_SORTIX=1
|
|
||||||
include ../build-aux/platform.mak
|
|
||||||
include ../build-aux/compiler.mak
|
|
||||||
include ../build-aux/version.mak
|
|
||||||
include ../build-aux/dirs.mak
|
|
||||||
|
|
||||||
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
|
||||||
CFLAGS?=$(OPTLEVEL)
|
|
||||||
|
|
||||||
CFLAGS:=$(CFLAGS) -Wall -Wextra
|
|
||||||
CPPFLAGS:=$(CPPFLAGS) -I include
|
|
||||||
|
|
||||||
CLIENT_OBJS:=\
|
|
||||||
client/library.o \
|
|
||||||
client/session.o \
|
|
||||||
client/window.o \
|
|
||||||
|
|
||||||
BINS:=client/libdispd.a
|
|
||||||
|
|
||||||
all: $(BINS)
|
|
||||||
|
|
||||||
.PHONY: all headers client clean install install-include-dirs \
|
|
||||||
install-headers install-client-dirs install-client
|
|
||||||
|
|
||||||
headers:
|
|
||||||
|
|
||||||
client: client/libdispd.a
|
|
||||||
|
|
||||||
client/libdispd.a: $(CLIENT_OBJS)
|
|
||||||
$(AR) rcs $@ $(CLIENT_OBJS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(CLIENT_OBJS)
|
|
||||||
rm -f $(BINS)
|
|
||||||
rm -f *.o client/*.o
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
# Installation into sysroot
|
|
||||||
install: install-headers install-client
|
|
||||||
|
|
||||||
install-include-dirs: headers
|
|
||||||
mkdir -p $(DESTDIR)$(INCLUDEDIR)
|
|
||||||
|
|
||||||
install-headers: install-include-dirs headers
|
|
||||||
cp -RTv include $(DESTDIR)$(INCLUDEDIR)
|
|
||||||
|
|
||||||
install-client-dirs:
|
|
||||||
mkdir -p $(DESTDIR)$(LIBDIR)
|
|
||||||
|
|
||||||
install-client: install-client-dirs client
|
|
||||||
cp -P client/libdispd.a $(DESTDIR)$(LIBDIR)
|
|
|
@ -1,130 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2012, 2013, 2014, 2016 Jonas 'Sortie' Termansen.
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*
|
|
||||||
* session.c
|
|
||||||
* Handles session management.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/display.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <err.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <dispd.h>
|
|
||||||
|
|
||||||
#include "session.h"
|
|
||||||
|
|
||||||
struct dispd_session* global_session = NULL;
|
|
||||||
|
|
||||||
bool dispd__session_initialize(int* argc, char*** argv)
|
|
||||||
{
|
|
||||||
(void) argc;
|
|
||||||
(void) argv;
|
|
||||||
size_t size = sizeof(struct dispd_session);
|
|
||||||
global_session = (struct dispd_session*) malloc(size);
|
|
||||||
if ( !global_session )
|
|
||||||
return false;
|
|
||||||
memset(global_session, 0, sizeof(*global_session));
|
|
||||||
int tty_fd = open("/dev/tty", O_RDWR);
|
|
||||||
if ( tty_fd < 0 )
|
|
||||||
return free(global_session), false;
|
|
||||||
struct tiocgdisplay display;
|
|
||||||
struct tiocgdisplays gdisplays;
|
|
||||||
memset(&gdisplays, 0, sizeof(gdisplays));
|
|
||||||
gdisplays.count = 1;
|
|
||||||
gdisplays.displays = &display;
|
|
||||||
bool fail = ioctl(tty_fd, TIOCGDISPLAYS, &gdisplays) < 0 ||
|
|
||||||
gdisplays.count == 0;
|
|
||||||
close(tty_fd);
|
|
||||||
if ( fail )
|
|
||||||
return free(global_session), false;
|
|
||||||
global_session->device = display.device;
|
|
||||||
global_session->connector = display.connector;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dispd_session* dispd_attach_default_session()
|
|
||||||
{
|
|
||||||
global_session->refcount++;
|
|
||||||
return global_session;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dispd_detach_session(struct dispd_session* session)
|
|
||||||
{
|
|
||||||
session->refcount--;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dispd_session_setup_game_rgba(struct dispd_session* session)
|
|
||||||
{
|
|
||||||
if ( session->is_rgba )
|
|
||||||
return true;
|
|
||||||
if ( session->current_window )
|
|
||||||
return false;
|
|
||||||
struct dispmsg_get_crtc_mode msg;
|
|
||||||
msg.msgid = DISPMSG_GET_CRTC_MODE;
|
|
||||||
msg.device = session->device;
|
|
||||||
msg.connector = session->connector;
|
|
||||||
if ( dispmsg_issue(&msg, sizeof(msg)) != 0 )
|
|
||||||
return false;
|
|
||||||
if ( !(msg.mode.control & 1) || msg.mode.fb_format != 32 )
|
|
||||||
{
|
|
||||||
pid_t childpid = fork();
|
|
||||||
if ( childpid < 0 )
|
|
||||||
return false;
|
|
||||||
if ( childpid )
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
waitpid(childpid, &status, 0);
|
|
||||||
return session->is_rgba = (WIFEXITED(status) && !WEXITSTATUS(status));
|
|
||||||
}
|
|
||||||
const char* chvideomode = "chvideomode";
|
|
||||||
#if 1
|
|
||||||
// TODO chvideomode currently launches --bpp 32 as a program...
|
|
||||||
execlp(chvideomode, chvideomode, (const char*) NULL);
|
|
||||||
#else
|
|
||||||
execlp(chvideomode, chvideomode,
|
|
||||||
"--bpp", "32",
|
|
||||||
"--show-graphics", "true",
|
|
||||||
"--show-text", "false",
|
|
||||||
(const char*) NULL);
|
|
||||||
#endif
|
|
||||||
err(127, "%s", chvideomode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK: The console may be rendered asynchronously and it might still be
|
|
||||||
// rendering to the framebuffer, however this process is about to do
|
|
||||||
// bitmapped graphics to the framebuffer as well. Since there is no
|
|
||||||
// synchronization with the terminal except not writing to it, there
|
|
||||||
// is a small window where both are fighting for the framebuffer.
|
|
||||||
// We can resolve this issue by simply fsync()ing the terminal, which
|
|
||||||
// causes the scheduled console rendering to finish before returning.
|
|
||||||
int tty_fd = open("/dev/tty", O_WRONLY);
|
|
||||||
if ( 0 <= tty_fd )
|
|
||||||
{
|
|
||||||
fsync(tty_fd);
|
|
||||||
close(tty_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return session->is_rgba = true;
|
|
||||||
}
|
|
|
@ -1,150 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2012, 2013, 2014, 2016 Jonas 'Sortie' Termansen.
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*
|
|
||||||
* window.c
|
|
||||||
* Handles windows.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/display.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <ioleast.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <dispd.h>
|
|
||||||
|
|
||||||
#include "framebuffer.h"
|
|
||||||
#include "session.h"
|
|
||||||
#include "window.h"
|
|
||||||
|
|
||||||
struct dispd_window* dispd_create_window_game_rgba(struct dispd_session* session)
|
|
||||||
{
|
|
||||||
if ( session->current_window )
|
|
||||||
return NULL;
|
|
||||||
if ( !session->is_rgba )
|
|
||||||
return NULL;
|
|
||||||
size_t size = sizeof(struct dispd_window);
|
|
||||||
struct dispd_window* window = (struct dispd_window*) malloc(size);
|
|
||||||
if ( !window )
|
|
||||||
return NULL;
|
|
||||||
memset(window, 0, sizeof(*window));
|
|
||||||
window->session = session;
|
|
||||||
return session->current_window = window;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dispd_destroy_window(struct dispd_window* window)
|
|
||||||
{
|
|
||||||
free(window->cached_buffer);
|
|
||||||
free(window);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t* request_buffer(struct dispd_window* window, size_t size)
|
|
||||||
{
|
|
||||||
if ( window->cached_buffer )
|
|
||||||
{
|
|
||||||
if ( window->cached_buffer_size == size )
|
|
||||||
{
|
|
||||||
uint8_t* ret = window->cached_buffer;
|
|
||||||
window->cached_buffer = NULL;
|
|
||||||
window->cached_buffer_size = 0;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
free(window->cached_buffer);
|
|
||||||
window->cached_buffer = NULL;
|
|
||||||
window->cached_buffer_size = 0;
|
|
||||||
}
|
|
||||||
return (uint8_t*) malloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void return_buffer(struct dispd_window* window, uint8_t* b, size_t size)
|
|
||||||
{
|
|
||||||
free(window->cached_buffer);
|
|
||||||
window->cached_buffer = b;
|
|
||||||
window->cached_buffer_size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dispd_framebuffer* dispd_begin_render(struct dispd_window* window)
|
|
||||||
{
|
|
||||||
struct dispmsg_get_crtc_mode msg;
|
|
||||||
msg.msgid = DISPMSG_GET_CRTC_MODE;
|
|
||||||
msg.device = window->session->device;
|
|
||||||
msg.connector = window->session->connector;
|
|
||||||
if ( dispmsg_issue(&msg, sizeof(msg)) != 0 )
|
|
||||||
return NULL;
|
|
||||||
size_t size = sizeof(struct dispd_framebuffer);
|
|
||||||
struct dispd_framebuffer* fb = (struct dispd_framebuffer*) malloc(size);
|
|
||||||
if ( !fb )
|
|
||||||
return NULL;
|
|
||||||
memset(fb, 0, sizeof(*fb));
|
|
||||||
fb->window = window;
|
|
||||||
fb->width = msg.mode.view_xres;
|
|
||||||
fb->height = msg.mode.view_yres;
|
|
||||||
fb->bpp = 32;
|
|
||||||
fb->pitch = fb->width * fb->bpp / 8;
|
|
||||||
fb->datasize = fb->pitch * fb->height;
|
|
||||||
fb->data = (uint8_t*) request_buffer(window, fb->datasize);
|
|
||||||
fb->fb_location = msg.mode.fb_location;
|
|
||||||
if ( !fb->data ) { free(fb); return NULL; }
|
|
||||||
return fb;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dispd_finish_render(struct dispd_framebuffer* fb)
|
|
||||||
{
|
|
||||||
struct dispd_window* window = fb->window;
|
|
||||||
bool ret = false;
|
|
||||||
struct dispmsg_write_memory msg;
|
|
||||||
msg.msgid = DISPMSG_WRITE_MEMORY;
|
|
||||||
msg.device = window->session->device;
|
|
||||||
msg.offset = fb->fb_location;
|
|
||||||
msg.size = fb->datasize;
|
|
||||||
msg.src = fb->data;
|
|
||||||
if ( dispmsg_issue(&msg, sizeof(msg)) == 0 )
|
|
||||||
ret = true;
|
|
||||||
return_buffer(window, fb->data, fb->datasize);
|
|
||||||
free(fb);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* dispd_get_framebuffer_data(struct dispd_framebuffer* fb)
|
|
||||||
{
|
|
||||||
return fb->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t dispd_get_framebuffer_pitch(struct dispd_framebuffer* fb)
|
|
||||||
{
|
|
||||||
return fb->pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dispd_get_framebuffer_format(struct dispd_framebuffer* fb)
|
|
||||||
{
|
|
||||||
return fb->bpp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dispd_get_framebuffer_height(struct dispd_framebuffer* fb)
|
|
||||||
{
|
|
||||||
return fb->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dispd_get_framebuffer_width(struct dispd_framebuffer* fb)
|
|
||||||
{
|
|
||||||
return fb->width;
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2012 Jonas 'Sortie' Termansen.
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*
|
|
||||||
* dispd.h
|
|
||||||
* Interface to the Sortix Display Daemon.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef INCLUDE_DISPD_H
|
|
||||||
#define INCLUDE_DISPD_H
|
|
||||||
|
|
||||||
#if !defined(__cplusplus)
|
|
||||||
#include <stdbool.h>
|
|
||||||
#endif
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct dispd_session;
|
|
||||||
struct dispd_window;
|
|
||||||
struct dispd_framebuffer;
|
|
||||||
|
|
||||||
bool dispd_initialize(int* argc, char*** argv);
|
|
||||||
|
|
||||||
struct dispd_session* dispd_attach_default_session(void);
|
|
||||||
bool dispd_detach_session(struct dispd_session* session);
|
|
||||||
|
|
||||||
bool dispd_session_setup_game_rgba(struct dispd_session* session);
|
|
||||||
|
|
||||||
struct dispd_window* dispd_create_window_game_rgba(struct dispd_session* session);
|
|
||||||
bool dispd_destroy_window(struct dispd_window* window);
|
|
||||||
|
|
||||||
struct dispd_framebuffer* dispd_begin_render(struct dispd_window* window);
|
|
||||||
bool dispd_finish_render(struct dispd_framebuffer* framebuffer);
|
|
||||||
|
|
||||||
uint8_t* dispd_get_framebuffer_data(struct dispd_framebuffer* framebuffer);
|
|
||||||
size_t dispd_get_framebuffer_pitch(struct dispd_framebuffer* framebuffer);
|
|
||||||
int dispd_get_framebuffer_format(struct dispd_framebuffer* framebuffer);
|
|
||||||
int dispd_get_framebuffer_height(struct dispd_framebuffer* framebuffer);
|
|
||||||
int dispd_get_framebuffer_width(struct dispd_framebuffer* framebuffer);
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
display
|
||||||
|
*.o
|
||||||
|
*.inc
|
|
@ -0,0 +1,51 @@
|
||||||
|
SOFTWARE_MEANT_FOR_SORTIX=1
|
||||||
|
include ../build-aux/platform.mak
|
||||||
|
include ../build-aux/compiler.mak
|
||||||
|
include ../build-aux/dirs.mak
|
||||||
|
|
||||||
|
OPTLEVEL?=-g -O2
|
||||||
|
CFLAGS?=$(OPTLEVEL)
|
||||||
|
|
||||||
|
CFLAGS:=$(CFLAGS) -Wall -Wextra
|
||||||
|
|
||||||
|
PROGRAM=display
|
||||||
|
MANPAGES1 = display.1
|
||||||
|
MANPAGES5 = displayrc.5
|
||||||
|
|
||||||
|
OBJS=\
|
||||||
|
connection.o \
|
||||||
|
display-code.o \
|
||||||
|
display.o \
|
||||||
|
server.o \
|
||||||
|
window.o \
|
||||||
|
|
||||||
|
LIBS:=-lui
|
||||||
|
|
||||||
|
all: $(PROGRAM)
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p $(DESTDIR)$(BINDIR)
|
||||||
|
install $(PROGRAM) $(DESTDIR)$(BINDIR)
|
||||||
|
mkdir -p $(DESTDIR)$(SYSCONFDIR)/default
|
||||||
|
printf '#!sh\nexec terminal\n' > $(DESTDIR)$(SYSCONFDIR)/default/displayrc
|
||||||
|
chmod +x $(DESTDIR)$(SYSCONFDIR)/default/displayrc
|
||||||
|
mkdir -p $(DESTDIR)$(MANDIR)/man1
|
||||||
|
install $(MANPAGES1) $(DESTDIR)$(MANDIR)/man1
|
||||||
|
mkdir -p $(DESTDIR)$(MANDIR)/man5
|
||||||
|
install $(MANPAGES5) $(DESTDIR)$(MANDIR)/man5
|
||||||
|
|
||||||
|
$(PROGRAM): $(OBJS)
|
||||||
|
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $(OBJS) -o $@ $(LIBS)
|
||||||
|
|
||||||
|
display.o: arrow.inc
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
%.inc: %.rgb
|
||||||
|
carray -cs -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(PROGRAM) *.o *.inc
|
Binary file not shown.
|
@ -0,0 +1,535 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
* Copyright (c) 2023 Juhani 'nortti' Krekelä.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* connection.c
|
||||||
|
* Display protocol implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/display.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/termmode.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
#include "display.h"
|
||||||
|
|
||||||
|
void connection_schedule_transmit(struct connection* connection,
|
||||||
|
const void* buffer,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
assert(connection);
|
||||||
|
size_t available = connection->outgoing_size - connection->outgoing_used;
|
||||||
|
if ( available < count )
|
||||||
|
{
|
||||||
|
// TODO: Overflow.
|
||||||
|
size_t required_size = connection->outgoing_used + count;
|
||||||
|
// TODO: Check allocation.
|
||||||
|
unsigned char* new_outgoing = malloc(required_size);
|
||||||
|
size_t first_available =
|
||||||
|
connection->outgoing_size - connection->outgoing_offset;
|
||||||
|
size_t first = connection->outgoing_used < first_available ?
|
||||||
|
connection->outgoing_used :
|
||||||
|
first_available;
|
||||||
|
if ( connection->outgoing )
|
||||||
|
{
|
||||||
|
memcpy(new_outgoing, connection->outgoing +
|
||||||
|
connection->outgoing_offset, first);
|
||||||
|
size_t second = connection->outgoing_used - first;
|
||||||
|
memcpy(new_outgoing + first, connection->outgoing, second);
|
||||||
|
free(connection->outgoing);
|
||||||
|
}
|
||||||
|
connection->outgoing_offset = 0;
|
||||||
|
connection->outgoing_size = required_size;
|
||||||
|
connection->outgoing = new_outgoing;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t used_offset =
|
||||||
|
(connection->outgoing_offset + connection->outgoing_used) %
|
||||||
|
connection->outgoing_size;
|
||||||
|
size_t first_available = connection->outgoing_size - used_offset;
|
||||||
|
size_t first = count < first_available ? count : first_available;
|
||||||
|
memcpy(connection->outgoing + used_offset, buffer, first);
|
||||||
|
size_t second = count - first;
|
||||||
|
memcpy(connection->outgoing, (const unsigned char*) buffer + first, second);
|
||||||
|
connection->outgoing_used += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_schedule_ack_event(struct connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
int32_t error)
|
||||||
|
{
|
||||||
|
struct event_ack event = { .id = id, .error = error };
|
||||||
|
struct display_packet_header header = { .id = EVENT_ACK,
|
||||||
|
.size = sizeof(event) };
|
||||||
|
connection_schedule_transmit(connection, &header, sizeof(header));
|
||||||
|
connection_schedule_transmit(connection, &event, sizeof(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_initialize(struct connection* connection,
|
||||||
|
struct display* display,
|
||||||
|
int fd)
|
||||||
|
{
|
||||||
|
memset(connection, 0, sizeof(*connection));
|
||||||
|
connection->display = display;
|
||||||
|
connection->fd = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct window* connection_find_window_raw(struct connection* connection,
|
||||||
|
uint32_t window_id)
|
||||||
|
{
|
||||||
|
if ( MAX_WINDOWS_PER_CONNECTION <= window_id )
|
||||||
|
return NULL;
|
||||||
|
return &connection->windows[window_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct window* connection_find_window(struct connection* connection,
|
||||||
|
uint32_t window_id)
|
||||||
|
{
|
||||||
|
struct window* result = connection_find_window_raw(connection, window_id);
|
||||||
|
if ( !result->created )
|
||||||
|
return NULL;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONNECTION_MESSAGE_HANDLER_NO_AUX(message_name) \
|
||||||
|
void connection_handler_##message_name( \
|
||||||
|
struct connection* connection, \
|
||||||
|
struct display_##message_name* msg, \
|
||||||
|
void* auxiliary __attribute__((unused)), \
|
||||||
|
size_t auxiliary_size __attribute__((unused)), \
|
||||||
|
const struct server* server __attribute__((unused)))
|
||||||
|
|
||||||
|
#define CONNECTION_MESSAGE_HANDLER(message_name) \
|
||||||
|
void connection_handler_##message_name( \
|
||||||
|
struct connection* connection, \
|
||||||
|
struct display_##message_name* msg, \
|
||||||
|
unsigned char* auxiliary, \
|
||||||
|
size_t auxiliary_size, \
|
||||||
|
const struct server* server __attribute__((unused)))
|
||||||
|
|
||||||
|
#define CONNECTION_MESSAGE_HANDLER_NO_AUX_SERVER(message_name) \
|
||||||
|
void connection_handler_##message_name( \
|
||||||
|
struct connection* connection, \
|
||||||
|
struct display_##message_name* msg, \
|
||||||
|
void* auxiliary __attribute__((unused)), \
|
||||||
|
size_t auxiliary_size __attribute__((unused)), \
|
||||||
|
const struct server* server)
|
||||||
|
|
||||||
|
#define CONNECTION_MESSAGE_HANDLER_SERVER(message_name) \
|
||||||
|
void connection_handler_##message_name( \
|
||||||
|
struct connection* connection, \
|
||||||
|
struct display_##message_name* msg, \
|
||||||
|
unsigned char* auxiliary, \
|
||||||
|
size_t auxiliary_size, \
|
||||||
|
const struct server* server)
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_NO_AUX(shutdown)
|
||||||
|
{
|
||||||
|
(void) connection;
|
||||||
|
if ( msg->code == 0 )
|
||||||
|
exit(0);
|
||||||
|
else if ( msg->code == 1 )
|
||||||
|
exit(1);
|
||||||
|
else if ( msg->code == 2 )
|
||||||
|
exit(2);
|
||||||
|
else if ( msg->code == 3 )
|
||||||
|
exit(3);
|
||||||
|
else
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_NO_AUX(create_window)
|
||||||
|
{
|
||||||
|
struct window* window =
|
||||||
|
connection_find_window_raw(connection, msg->window_id);
|
||||||
|
if ( !window )
|
||||||
|
return;
|
||||||
|
if ( window->created )
|
||||||
|
return;
|
||||||
|
window_initialize(window, connection, connection->display, msg->window_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_NO_AUX(destroy_window)
|
||||||
|
{
|
||||||
|
struct window* window = connection_find_window(connection, msg->window_id);
|
||||||
|
if ( !window )
|
||||||
|
return;
|
||||||
|
window_destroy(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_NO_AUX(resize_window)
|
||||||
|
{
|
||||||
|
struct window* window = connection_find_window(connection, msg->window_id);
|
||||||
|
if ( !window )
|
||||||
|
return;
|
||||||
|
window_client_resize(window, msg->width, msg->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER(render_window)
|
||||||
|
{
|
||||||
|
struct window* window = connection_find_window(connection, msg->window_id);
|
||||||
|
if ( !window )
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct framebuffer src;
|
||||||
|
src.xres = msg->width;
|
||||||
|
src.yres = msg->height;
|
||||||
|
src.pitch = msg->width;
|
||||||
|
src.buffer = (uint32_t*) auxiliary;
|
||||||
|
|
||||||
|
if ( auxiliary_size < sizeof(uint32_t) * src.xres * src.yres )
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct framebuffer dst =
|
||||||
|
framebuffer_crop(window_client_buffer(window),
|
||||||
|
msg->left, msg->top, msg->width, msg->height);
|
||||||
|
|
||||||
|
framebuffer_copy_to_framebuffer(dst, src);
|
||||||
|
|
||||||
|
window_schedule_redraw(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER(title_window)
|
||||||
|
{
|
||||||
|
struct window* window = connection_find_window(connection, msg->window_id);
|
||||||
|
if ( !window )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const char* title = (char*) auxiliary;
|
||||||
|
free(window->title);
|
||||||
|
window->title = strndup(title, auxiliary_size);
|
||||||
|
|
||||||
|
window_render_frame(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_NO_AUX(show_window)
|
||||||
|
{
|
||||||
|
struct window* window = connection_find_window(connection, msg->window_id);
|
||||||
|
if ( !window )
|
||||||
|
return;
|
||||||
|
if ( !window->show )
|
||||||
|
display_schedule_redraw(window->display);
|
||||||
|
window->show = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_NO_AUX(hide_window)
|
||||||
|
{
|
||||||
|
struct window* window = connection_find_window(connection, msg->window_id);
|
||||||
|
if ( !window )
|
||||||
|
return;
|
||||||
|
if ( window->show )
|
||||||
|
display_schedule_redraw(window->display);
|
||||||
|
window->show = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_SERVER(chkblayout)
|
||||||
|
{
|
||||||
|
if ( tcsetblob(server->tty_fd, "kblayout", auxiliary, auxiliary_size) < 0 )
|
||||||
|
connection_schedule_ack_event(connection, msg->id, errno);
|
||||||
|
else
|
||||||
|
connection_schedule_ack_event(connection, msg->id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_NO_AUX(request_displays)
|
||||||
|
{
|
||||||
|
struct event_displays event;
|
||||||
|
event.id = msg->id;
|
||||||
|
event.displays = 1; // TODO: Multimonitor support.
|
||||||
|
|
||||||
|
struct display_packet_header header;
|
||||||
|
header.id = EVENT_DISPLAYS;
|
||||||
|
header.size = sizeof(event);
|
||||||
|
|
||||||
|
connection_schedule_transmit(connection, &header, sizeof(header));
|
||||||
|
connection_schedule_transmit(connection, &event, sizeof(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dispmsg_crtc_mode*
|
||||||
|
get_available_modes(const struct tiocgdisplay* display,
|
||||||
|
size_t* modes_count_ptr)
|
||||||
|
{
|
||||||
|
struct dispmsg_get_crtc_modes msg;
|
||||||
|
msg.msgid = DISPMSG_GET_CRTC_MODES;
|
||||||
|
msg.device = display->device;
|
||||||
|
msg.connector = display->connector;
|
||||||
|
size_t guess = 1;
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
struct dispmsg_crtc_mode* ret =
|
||||||
|
calloc(guess, sizeof(struct dispmsg_crtc_mode));
|
||||||
|
if ( !ret )
|
||||||
|
return NULL;
|
||||||
|
msg.modes_length = guess;
|
||||||
|
msg.modes = ret;
|
||||||
|
if ( dispmsg_issue(&msg, sizeof(msg)) == 0 )
|
||||||
|
{
|
||||||
|
*modes_count_ptr = guess;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
free(ret);
|
||||||
|
if ( errno == ERANGE && guess < msg.modes_length )
|
||||||
|
{
|
||||||
|
guess = msg.modes_length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_NO_AUX_SERVER(request_display_modes)
|
||||||
|
{
|
||||||
|
struct event_display_modes event;
|
||||||
|
event.id = msg->id;
|
||||||
|
event.modes_count = 0;
|
||||||
|
|
||||||
|
struct dispmsg_crtc_mode* modes = NULL;
|
||||||
|
// TODO: Multimonitor support.
|
||||||
|
if ( msg->display_id != 0 )
|
||||||
|
{
|
||||||
|
connection_schedule_ack_event(connection, msg->id, EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t modes_count;
|
||||||
|
modes = get_available_modes(&server->display->display, &modes_count);
|
||||||
|
if ( !modes )
|
||||||
|
{
|
||||||
|
connection_schedule_ack_event(connection, msg->id, errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( (uint32_t) modes_count != modes_count )
|
||||||
|
{
|
||||||
|
free(modes);
|
||||||
|
connection_schedule_ack_event(connection, msg->id, EOVERFLOW);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.modes_count = modes_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t modes_size = event.modes_count * sizeof(struct dispmsg_crtc_mode);
|
||||||
|
|
||||||
|
struct display_packet_header header;
|
||||||
|
header.id = EVENT_DISPLAY_MODES;
|
||||||
|
header.size = sizeof(event) + modes_size;
|
||||||
|
|
||||||
|
connection_schedule_transmit(connection, &header, sizeof(header));
|
||||||
|
connection_schedule_transmit(connection, &event, sizeof(event));
|
||||||
|
connection_schedule_transmit(connection, modes, modes_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_current_mode(const struct tiocgdisplay* display,
|
||||||
|
struct dispmsg_crtc_mode* mode)
|
||||||
|
{
|
||||||
|
struct dispmsg_set_crtc_mode msg;
|
||||||
|
msg.msgid = DISPMSG_GET_CRTC_MODE;
|
||||||
|
msg.device = display->device;
|
||||||
|
msg.connector = display->connector;
|
||||||
|
if ( dispmsg_issue(&msg, sizeof(msg)) )
|
||||||
|
return false;
|
||||||
|
*mode = msg.mode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_NO_AUX_SERVER(request_display_mode)
|
||||||
|
{
|
||||||
|
struct event_display_mode event;
|
||||||
|
event.id = msg->id;
|
||||||
|
|
||||||
|
// TODO: Multimonitor support.
|
||||||
|
if ( msg->display_id != 0 )
|
||||||
|
{
|
||||||
|
connection_schedule_ack_event(connection, msg->id, EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( !get_current_mode(&server->display->display, &event.mode) )
|
||||||
|
{
|
||||||
|
connection_schedule_ack_event(connection, msg->id, EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct display_packet_header header;
|
||||||
|
header.id = EVENT_DISPLAY_MODE;
|
||||||
|
header.size = sizeof(event);
|
||||||
|
|
||||||
|
connection_schedule_transmit(connection, &header, sizeof(header));
|
||||||
|
connection_schedule_transmit(connection, &event, sizeof(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool set_current_mode(const struct tiocgdisplay* display,
|
||||||
|
struct dispmsg_crtc_mode mode)
|
||||||
|
{
|
||||||
|
struct dispmsg_set_crtc_mode msg;
|
||||||
|
msg.msgid = DISPMSG_SET_CRTC_MODE;
|
||||||
|
msg.device = display->device;
|
||||||
|
msg.connector = display->connector;
|
||||||
|
msg.mode = mode;
|
||||||
|
return dispmsg_issue(&msg, sizeof(msg)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_MESSAGE_HANDLER_NO_AUX_SERVER(set_display_mode)
|
||||||
|
{
|
||||||
|
// TODO: Multimonitor support.
|
||||||
|
if ( msg->display_id != 0 )
|
||||||
|
connection_schedule_ack_event(connection, msg->id, EINVAL);
|
||||||
|
else if ( !set_current_mode(&server->display->display, msg->mode) )
|
||||||
|
connection_schedule_ack_event(connection, msg->id, errno);
|
||||||
|
else
|
||||||
|
connection_schedule_ack_event(connection, msg->id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (*connection_message_handler)(struct connection* connection,
|
||||||
|
void* msg,
|
||||||
|
void* auxiliary,
|
||||||
|
size_t auxiliary_size,
|
||||||
|
const struct server* server);
|
||||||
|
|
||||||
|
struct connection_message_handler_registration
|
||||||
|
{
|
||||||
|
connection_message_handler handler;
|
||||||
|
size_t message_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define REGISTER_CONNECTION_MESSAGE_HANDLER(message_name) \
|
||||||
|
{ (connection_message_handler) connection_handler_##message_name, \
|
||||||
|
sizeof(struct display_##message_name) }
|
||||||
|
|
||||||
|
struct connection_message_handler_registration connection_message_handlers[] =
|
||||||
|
{
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(create_window),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(destroy_window),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(resize_window),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(render_window),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(title_window),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(show_window),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(hide_window),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(shutdown),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(chkblayout),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(request_displays),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(request_display_modes),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(request_display_mode),
|
||||||
|
REGISTER_CONNECTION_MESSAGE_HANDLER(set_display_mode),
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t num_connection_message_handlers = sizeof(connection_message_handlers) /
|
||||||
|
sizeof(connection_message_handlers[0]);
|
||||||
|
|
||||||
|
short connection_interested_poll_events(struct connection* connection)
|
||||||
|
{
|
||||||
|
return POLLIN | (connection->outgoing_used ? POLLOUT : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_can_read(struct connection* connection,
|
||||||
|
const struct server* server)
|
||||||
|
{
|
||||||
|
while ( connection->packet_header_received <
|
||||||
|
sizeof(connection->packet_header) )
|
||||||
|
{
|
||||||
|
ssize_t amount = read(connection->fd,
|
||||||
|
(unsigned char*) &connection->packet_header +
|
||||||
|
connection->packet_header_received,
|
||||||
|
sizeof(connection->packet_header) -
|
||||||
|
connection->packet_header_received);
|
||||||
|
if ( amount < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) )
|
||||||
|
return;
|
||||||
|
if ( amount < 0 || amount == 0 )
|
||||||
|
return; // TODO: No longer signal interest in reading + disconnect.
|
||||||
|
connection->packet_header_received += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t packet_size = connection->packet_header.size;
|
||||||
|
|
||||||
|
// TODO: Check allocation and protect against too big buffers.
|
||||||
|
if ( !connection->packet )
|
||||||
|
connection->packet = malloc(packet_size);
|
||||||
|
|
||||||
|
while ( connection->packet_received < packet_size )
|
||||||
|
{
|
||||||
|
ssize_t amount = read(connection->fd,
|
||||||
|
connection->packet + connection->packet_received,
|
||||||
|
packet_size - connection->packet_received);
|
||||||
|
if ( amount < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) )
|
||||||
|
return;
|
||||||
|
if ( amount < 0 || amount == 0 )
|
||||||
|
return; // TODO: No longer signal interest in reading + disconnect.
|
||||||
|
connection->packet_received += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t packet_id = connection->packet_header.id;
|
||||||
|
|
||||||
|
if ( packet_id < num_connection_message_handlers )
|
||||||
|
{
|
||||||
|
struct connection_message_handler_registration* handler =
|
||||||
|
&connection_message_handlers[packet_id];
|
||||||
|
unsigned char* auxiliary = connection->packet + handler->message_size;
|
||||||
|
size_t auxiliary_size = packet_size - handler->message_size;
|
||||||
|
handler->handler(connection, connection->packet,
|
||||||
|
auxiliary, auxiliary_size, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection->packet_header_received = 0;
|
||||||
|
free(connection->packet);
|
||||||
|
connection->packet = NULL;
|
||||||
|
connection->packet_received = 0;
|
||||||
|
|
||||||
|
// TODO: Check if we can receive another packet, but only if we haven't
|
||||||
|
// done so much work that the display server is starved.
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_can_write(struct connection* connection)
|
||||||
|
{
|
||||||
|
while ( connection->outgoing_used )
|
||||||
|
{
|
||||||
|
size_t available =
|
||||||
|
connection->outgoing_size - connection->outgoing_offset;
|
||||||
|
size_t count = connection->outgoing_used < available ?
|
||||||
|
connection->outgoing_used : available;
|
||||||
|
unsigned char* buf = connection->outgoing + connection->outgoing_offset;
|
||||||
|
ssize_t amount = write(connection->fd, buf, count);
|
||||||
|
if ( amount < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) )
|
||||||
|
return;
|
||||||
|
if ( amount < 0 || amount == 0 )
|
||||||
|
return; // TODO: No longer signal interest in writing + disconnect.
|
||||||
|
connection->outgoing_offset = (connection->outgoing_offset + amount) %
|
||||||
|
connection->outgoing_size;
|
||||||
|
connection->outgoing_used -= amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(connection->outgoing);
|
||||||
|
connection->outgoing = NULL;
|
||||||
|
connection->outgoing_used = 0;
|
||||||
|
connection->outgoing_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_destroy(struct connection* connection)
|
||||||
|
{
|
||||||
|
for ( size_t i = 0; i < MAX_WINDOWS_PER_CONNECTION; i++ )
|
||||||
|
{
|
||||||
|
if ( !connection->windows[i].created )
|
||||||
|
continue;
|
||||||
|
window_destroy(&connection->windows[i]);
|
||||||
|
}
|
||||||
|
close(connection->fd);
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* connection.h
|
||||||
|
* Display protocol implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTION_H
|
||||||
|
#define CONNECTION_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <display-protocol.h>
|
||||||
|
|
||||||
|
#include "server.h"
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
struct display;
|
||||||
|
struct window;
|
||||||
|
|
||||||
|
#define MAX_WINDOWS_PER_CONNECTION 256
|
||||||
|
|
||||||
|
struct connection
|
||||||
|
{
|
||||||
|
struct display* display;
|
||||||
|
struct window windows[MAX_WINDOWS_PER_CONNECTION];
|
||||||
|
struct display_packet_header packet_header;
|
||||||
|
size_t packet_header_received;
|
||||||
|
unsigned char* packet;
|
||||||
|
size_t packet_received;
|
||||||
|
unsigned char* outgoing;
|
||||||
|
size_t outgoing_offset;
|
||||||
|
size_t outgoing_used;
|
||||||
|
size_t outgoing_size;
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
void connection_schedule_transmit(struct connection* connection,
|
||||||
|
const void* buffer,
|
||||||
|
size_t count);
|
||||||
|
void connection_initialize(struct connection* connection,
|
||||||
|
struct display* display,
|
||||||
|
int fd);
|
||||||
|
struct window* connection_find_window_raw(struct connection* connection,
|
||||||
|
uint32_t window_id);
|
||||||
|
struct window* connection_find_window(struct connection* connection,
|
||||||
|
uint32_t window_id);
|
||||||
|
short connection_interested_poll_events(struct connection* connection);
|
||||||
|
void connection_can_read(struct connection* connection,
|
||||||
|
const struct server* server);
|
||||||
|
void connection_can_write(struct connection* connection);
|
||||||
|
void connection_destroy(struct connection* connection);
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -13,26 +13,27 @@
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*
|
*
|
||||||
* window.h
|
* damage-rect.c
|
||||||
* Handles windows.
|
* Damage rectangles.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef INCLUDE_DISPD_WINDOW_H
|
#include <stddef.h>
|
||||||
#define INCLUDE_DISPD_WINDOW_H
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#include "damage-rect.h"
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct dispd_window
|
struct damage_rect damage_rect_add(struct damage_rect a, struct damage_rect b)
|
||||||
{
|
{
|
||||||
struct dispd_session* session;
|
if ( !a.width || !a.height )
|
||||||
uint8_t* cached_buffer;
|
return b;
|
||||||
size_t cached_buffer_size;
|
if ( !b.width || !b.height )
|
||||||
};
|
return a;
|
||||||
|
if ( a.left < b.left )
|
||||||
#if defined(__cplusplus)
|
b.width += b.left - a.left, b.left = a.left;
|
||||||
} /* extern "C" */
|
if ( b.width < a.width )
|
||||||
#endif
|
b.width = a.width;
|
||||||
|
if ( a.top < b.top )
|
||||||
#endif
|
b.height += b.top - a.top, b.top = a.top;
|
||||||
|
if ( b.height < a.height )
|
||||||
|
b.height = a.height;
|
||||||
|
return b;
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -13,21 +13,23 @@
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*
|
*
|
||||||
* library.c
|
* damage-rect.h
|
||||||
* Main entry point of the Sortix Display Daemon.
|
* Damage rectangles.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdbool.h>
|
#ifndef DAMAGE_RECT_H
|
||||||
|
#define DAMAGE_RECT_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <dispd.h>
|
struct damage_rect
|
||||||
|
|
||||||
#include "session.h"
|
|
||||||
|
|
||||||
bool dispd_initialize(int* argc, char*** argv)
|
|
||||||
{
|
{
|
||||||
if ( !dispd__session_initialize(argc, argv) )
|
size_t left;
|
||||||
return false;
|
size_t top;
|
||||||
return true;
|
size_t width;
|
||||||
}
|
size_t height;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct damage_rect damage_rect_add(struct damage_rect a, struct damage_rect b);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,917 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2018, 2022, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* display-code.c
|
||||||
|
* Display server logic.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/display.h>
|
||||||
|
#include <sys/keycodes.h>
|
||||||
|
#include <sys/ps2mouse.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <timespec.h>
|
||||||
|
|
||||||
|
#include <display-protocol.h>
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
#include "display.h"
|
||||||
|
#include "pixel.h"
|
||||||
|
#include "vgafont.h"
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
extern struct framebuffer arrow_framebuffer;
|
||||||
|
|
||||||
|
void display_initialize(struct display* display)
|
||||||
|
{
|
||||||
|
memset(display, 0, sizeof(*display));
|
||||||
|
display->redraw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_is_well_formed_display_list(struct display* display)
|
||||||
|
{
|
||||||
|
struct window* last_window = NULL;
|
||||||
|
struct window* iterator = display->bottom_window;
|
||||||
|
while ( iterator )
|
||||||
|
{
|
||||||
|
assert(iterator->below_window == last_window);
|
||||||
|
last_window = iterator;
|
||||||
|
iterator = iterator->above_window;
|
||||||
|
}
|
||||||
|
assert(last_window == display->top_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_is_well_formed_display(struct display* display)
|
||||||
|
{
|
||||||
|
assert_is_well_formed_display_list(display);
|
||||||
|
|
||||||
|
bool found_active_window = display->active_window == NULL;
|
||||||
|
bool found_tab_candidate = display->tab_candidate == NULL;
|
||||||
|
struct window* iterator = display->bottom_window;
|
||||||
|
while ( iterator )
|
||||||
|
{
|
||||||
|
if ( iterator == display->active_window )
|
||||||
|
found_active_window = true;
|
||||||
|
if ( iterator == display->tab_candidate )
|
||||||
|
found_tab_candidate = true;
|
||||||
|
iterator = iterator->above_window;
|
||||||
|
}
|
||||||
|
assert(found_active_window);
|
||||||
|
assert(found_tab_candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_link_window_at_top(struct display* display, struct window* window)
|
||||||
|
{
|
||||||
|
assert_is_well_formed_display_list(display);
|
||||||
|
|
||||||
|
assert(!window->above_window);
|
||||||
|
assert(!window->below_window);
|
||||||
|
assert(display->top_window != window);
|
||||||
|
assert(display->bottom_window != window);
|
||||||
|
|
||||||
|
if ( (window->below_window = display->top_window) )
|
||||||
|
window->below_window->above_window = window;
|
||||||
|
window->above_window = NULL;
|
||||||
|
|
||||||
|
display->top_window = window;
|
||||||
|
if ( !display->bottom_window )
|
||||||
|
display->bottom_window = window;
|
||||||
|
|
||||||
|
assert_is_well_formed_display_list(display);
|
||||||
|
|
||||||
|
display_schedule_redraw(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_unlink_window(struct display* display, struct window* window)
|
||||||
|
{
|
||||||
|
assert_is_well_formed_display_list(display);
|
||||||
|
|
||||||
|
assert(window->below_window || display->bottom_window == window);
|
||||||
|
assert(window->above_window || display->top_window == window);
|
||||||
|
|
||||||
|
if ( window->below_window )
|
||||||
|
window->below_window->above_window = window->above_window;
|
||||||
|
else
|
||||||
|
display->bottom_window = window->above_window;
|
||||||
|
if ( window->above_window )
|
||||||
|
window->above_window->below_window = window->below_window;
|
||||||
|
else
|
||||||
|
display->top_window = window->below_window;
|
||||||
|
|
||||||
|
assert(display->bottom_window != window);
|
||||||
|
assert(display->top_window != window);
|
||||||
|
|
||||||
|
window->above_window = NULL;
|
||||||
|
window->below_window = NULL;
|
||||||
|
|
||||||
|
assert_is_well_formed_display_list(display);
|
||||||
|
|
||||||
|
display_schedule_redraw(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_unlink_window_removal(struct display* display,
|
||||||
|
struct window* window)
|
||||||
|
{
|
||||||
|
assert_is_well_formed_display_list(display);
|
||||||
|
|
||||||
|
if ( display->tab_candidate == window )
|
||||||
|
if ( !(display->tab_candidate = window->below_window) )
|
||||||
|
if ( (display->tab_candidate = display->top_window) == window )
|
||||||
|
display->tab_candidate = NULL;
|
||||||
|
|
||||||
|
if ( display->active_window == window )
|
||||||
|
display->active_window = NULL;
|
||||||
|
|
||||||
|
window->focus = false;
|
||||||
|
display_schedule_redraw(display);
|
||||||
|
|
||||||
|
assert_is_well_formed_display_list(display);
|
||||||
|
|
||||||
|
display_unlink_window(display, window);
|
||||||
|
|
||||||
|
assert_is_well_formed_display_list(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_unmark_active_window(struct display* display,
|
||||||
|
struct window* window)
|
||||||
|
{
|
||||||
|
assert(display->active_window == window);
|
||||||
|
window->focus = false;
|
||||||
|
display->active_window = NULL;
|
||||||
|
window_render_frame(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_mark_active_window(struct display* display, struct window* window)
|
||||||
|
{
|
||||||
|
assert(!display->active_window);
|
||||||
|
window->focus = true;
|
||||||
|
display->active_window = window;
|
||||||
|
window_render_frame(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_update_active_window(struct display* display)
|
||||||
|
{
|
||||||
|
if ( !display->active_window && display->top_window )
|
||||||
|
display_mark_active_window(display, display->top_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_move_window_to_top(struct display* display, struct window* window)
|
||||||
|
{
|
||||||
|
display_unlink_window(display, window);
|
||||||
|
display_link_window_at_top(display, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_change_active_window(struct display* display,
|
||||||
|
struct window* window)
|
||||||
|
{
|
||||||
|
if ( display->active_window == window )
|
||||||
|
{
|
||||||
|
display_move_window_to_top(display, window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
display_unmark_active_window(display, display->active_window);
|
||||||
|
display_mark_active_window(display, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_set_active_window(struct display* display, struct window* window)
|
||||||
|
{
|
||||||
|
display_change_active_window(display, window);
|
||||||
|
display_move_window_to_top(display, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_add_window(struct display* display, struct window* window)
|
||||||
|
{
|
||||||
|
display_link_window_at_top(display, window);
|
||||||
|
display_update_active_window(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_remove_window(struct display* display, struct window* window)
|
||||||
|
{
|
||||||
|
display_unlink_window_removal(display, window);
|
||||||
|
display_update_active_window(display);
|
||||||
|
|
||||||
|
assert(display->top_window != window);
|
||||||
|
assert(display->bottom_window != window);
|
||||||
|
struct window* last_window = NULL;
|
||||||
|
struct window* iterator = display->bottom_window;
|
||||||
|
while ( iterator )
|
||||||
|
{
|
||||||
|
assert(iterator != window);
|
||||||
|
assert(iterator->below_window == last_window);
|
||||||
|
last_window = iterator;
|
||||||
|
iterator = iterator->above_window;
|
||||||
|
}
|
||||||
|
assert(last_window == display->top_window);
|
||||||
|
|
||||||
|
assert(!display->top_window || display->active_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
union c { struct { uint8_t b; uint8_t g; uint8_t r; }; uint32_t v; };
|
||||||
|
|
||||||
|
static void wallpaper(struct framebuffer fb)
|
||||||
|
{
|
||||||
|
static uint32_t s;
|
||||||
|
static uint32_t t;
|
||||||
|
static bool seeded = false;
|
||||||
|
if ( !seeded )
|
||||||
|
{
|
||||||
|
s = arc4random();
|
||||||
|
t = arc4random();
|
||||||
|
seeded = true;
|
||||||
|
}
|
||||||
|
for ( size_t y = 0; y < fb.yres; y++ )
|
||||||
|
{
|
||||||
|
for ( size_t x = 0; x < fb.xres; x++ )
|
||||||
|
{
|
||||||
|
uint32_t r = 3793 * x + 6959 * y + 1889 * t + 7901 * s;
|
||||||
|
r ^= (5717 * x * 2953 * y) ^ s ^ t;
|
||||||
|
r = (r >> 24) ^ (r >> 16) ^ (r >> 8) ^ r;
|
||||||
|
union c c;
|
||||||
|
if ( x && (r & 0x3) == 2 )
|
||||||
|
c.v = framebuffer_get_pixel(fb, x - 1, y);
|
||||||
|
else if ( y && (r & 0x3) == 1 )
|
||||||
|
c.v = framebuffer_get_pixel(fb, x, y - 1);
|
||||||
|
else if ( x && y )
|
||||||
|
c.v = framebuffer_get_pixel(fb, x - 1, y - 1);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c.v = t;
|
||||||
|
c.r = (c.r & 0xc0) | (r >> 0 & 0x3f);
|
||||||
|
c.g = (c.g & 0xc0) | (r >> 4 & 0x3f);
|
||||||
|
c.b = (c.b & 0xc0) | (r >> 8 & 0x3f);
|
||||||
|
}
|
||||||
|
if ( (r & 0xf0) == 0x10 && c.r ) c.r--;
|
||||||
|
if ( (r & 0xf0) == 0x20 && c.g ) c.g--;
|
||||||
|
if ( (r & 0xf0) == 0x30 && c.b ) c.b--;
|
||||||
|
if ( (r & 0xf0) == 0x40 && c.r != 255 ) c.r++;
|
||||||
|
if ( (r & 0xf0) == 0x50 && c.g != 255 ) c.g++;
|
||||||
|
if ( (r & 0xf0) == 0x60 && c.b != 255 ) c.b++;
|
||||||
|
union c tc = {.v = t};
|
||||||
|
if ( c.r && c.r - tc.r > (int8_t) (r >> 0) + 64 ) c.r--;
|
||||||
|
if ( c.r != 255 && tc.r - c.r > (int8_t) (r >> 4) + 240 ) c.r++;
|
||||||
|
if ( c.g && c.g - tc.g > (int8_t) (r >> 8) + 64) c.g--;
|
||||||
|
if ( c.g != 255 && tc.g - c.g > (int8_t) (r >> 12) + 240 ) c.g++;
|
||||||
|
if ( c.b && c.b - tc.b > (int8_t) (r >> 16) + 64 ) c.b--;
|
||||||
|
if ( c.b != 255 && tc.b - c.b > (int8_t) (r >> 20) + 240 ) c.b++;
|
||||||
|
framebuffer_set_pixel(fb, x, y, c.v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_composit(struct display* display, struct framebuffer fb)
|
||||||
|
{
|
||||||
|
struct damage_rect damage_rect = display->damage_rect;
|
||||||
|
damage_rect.left = 0;
|
||||||
|
damage_rect.top = 0;
|
||||||
|
damage_rect.width = fb.xres;
|
||||||
|
damage_rect.height = fb.yres;
|
||||||
|
if ( !damage_rect.width || !damage_rect.height )
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
uint32_t bg_color = make_color(0x89 * 2/3, 0xc7 * 2/3, 0xff * 2/3);
|
||||||
|
for ( size_t y = 0; y < damage_rect.height; y++ )
|
||||||
|
for ( size_t x = 0; x < damage_rect.width; x++ )
|
||||||
|
framebuffer_set_pixel(fb, damage_rect.left + x, damage_rect.top + y,
|
||||||
|
bg_color);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
framebuffer_copy_to_framebuffer(fb, display->wallpaper);
|
||||||
|
|
||||||
|
for ( struct window* window = display->bottom_window;
|
||||||
|
window;
|
||||||
|
window = window->above_window )
|
||||||
|
{
|
||||||
|
if ( !window->show )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
size_t winfb_left;
|
||||||
|
size_t winfb_top;
|
||||||
|
struct framebuffer winfb = window->buffer;
|
||||||
|
|
||||||
|
if ( window->left < 0 )
|
||||||
|
{
|
||||||
|
winfb_left = 0;
|
||||||
|
winfb = framebuffer_crop(winfb, -window->left, 0,
|
||||||
|
winfb.xres, winfb.yres);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
winfb_left = window->left;
|
||||||
|
|
||||||
|
if ( window->top < 0 )
|
||||||
|
{
|
||||||
|
winfb_top = 0;
|
||||||
|
winfb = framebuffer_crop(winfb, 0, -window->top,
|
||||||
|
winfb.xres, winfb.yres);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
winfb_top = window->top;
|
||||||
|
|
||||||
|
size_t winfb_width = winfb.xres;
|
||||||
|
size_t winfb_height = winfb.yres;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if ( winfb_left < damage_rect.left &&
|
||||||
|
winfb_width < damage_rect.left - winfb_left )
|
||||||
|
continue;
|
||||||
|
if ( winfb_left < damage_rect.left )
|
||||||
|
{
|
||||||
|
winfb_left = damage_rect.left;
|
||||||
|
winfb_width -= damage_rect.left - winfb_left;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct framebuffer fb_dst =
|
||||||
|
framebuffer_crop(fb, winfb_left, winfb_top,
|
||||||
|
winfb_width, winfb_height);
|
||||||
|
|
||||||
|
framebuffer_copy_to_framebuffer_blend(fb_dst, winfb);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* cursor_text = NULL;
|
||||||
|
switch ( display->mouse_state )
|
||||||
|
{
|
||||||
|
case MOUSE_STATE_NONE: break;
|
||||||
|
case MOUSE_STATE_IGNORE: break;
|
||||||
|
case MOUSE_STATE_BUTTON_PRESS: break;
|
||||||
|
case MOUSE_STATE_TITLE_MOVE: break;
|
||||||
|
case MOUSE_STATE_RESIZE_BOTTOM: cursor_text = "↓"; break;
|
||||||
|
case MOUSE_STATE_RESIZE_BOTTOM_LEFT: cursor_text = "└"; break;
|
||||||
|
case MOUSE_STATE_RESIZE_BOTTOM_RIGHT: cursor_text = "┘"; break;
|
||||||
|
case MOUSE_STATE_RESIZE_LEFT: cursor_text = "←"; break;
|
||||||
|
case MOUSE_STATE_RESIZE_RIGHT: cursor_text = "→"; break;
|
||||||
|
case MOUSE_STATE_RESIZE_TOP: cursor_text = "↑"; break;
|
||||||
|
case MOUSE_STATE_RESIZE_TOP_LEFT: cursor_text = "┌"; break;
|
||||||
|
case MOUSE_STATE_RESIZE_TOP_RIGHT: cursor_text = "┐"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pointer_hwidth = arrow_framebuffer.xres / 2;
|
||||||
|
int pointer_hheight = arrow_framebuffer.yres / 2;
|
||||||
|
|
||||||
|
int pointer_x = display->pointer_x - (cursor_text ? 0 : pointer_hwidth);
|
||||||
|
int pointer_y = display->pointer_y - (cursor_text ? 0 : pointer_hheight);
|
||||||
|
|
||||||
|
struct framebuffer arrow_render = arrow_framebuffer;
|
||||||
|
if ( pointer_x < 0 )
|
||||||
|
{
|
||||||
|
arrow_render = framebuffer_crop(arrow_render, -pointer_x, 0,
|
||||||
|
arrow_render.xres, arrow_render.yres);
|
||||||
|
pointer_x = 0;
|
||||||
|
}
|
||||||
|
if ( pointer_y < 0 )
|
||||||
|
{
|
||||||
|
arrow_render = framebuffer_crop(arrow_render, 0, -pointer_y,
|
||||||
|
arrow_render.xres, arrow_render.yres);
|
||||||
|
pointer_y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct framebuffer fb_dst =
|
||||||
|
framebuffer_crop(fb, pointer_x, pointer_y, fb.xres, fb.yres);
|
||||||
|
|
||||||
|
if ( cursor_text != NULL )
|
||||||
|
render_text(fb_dst, cursor_text, make_color(0, 0, 0));
|
||||||
|
else
|
||||||
|
framebuffer_copy_to_framebuffer_blend(fb_dst, arrow_render);
|
||||||
|
|
||||||
|
memset(&damage_rect, 0, sizeof(damage_rect));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_schedule_redraw(struct display* display)
|
||||||
|
{
|
||||||
|
display->redraw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_render(struct display* display)
|
||||||
|
{
|
||||||
|
if ( !display->redraw )
|
||||||
|
return;
|
||||||
|
display->redraw = false;
|
||||||
|
|
||||||
|
struct dispmsg_get_crtc_mode get_mode_msg = {0};
|
||||||
|
get_mode_msg.msgid = DISPMSG_GET_CRTC_MODE;
|
||||||
|
get_mode_msg.device = display->display.device;
|
||||||
|
get_mode_msg.connector = display->display.connector;
|
||||||
|
if ( dispmsg_issue(&get_mode_msg, sizeof(get_mode_msg)) != 0 )
|
||||||
|
err(1, "dispmsg_issue: dispmsg_get_crtc_mode");
|
||||||
|
struct dispmsg_crtc_mode mode = get_mode_msg.mode;
|
||||||
|
|
||||||
|
if ( !(mode.control & DISPMSG_CONTROL_VALID) )
|
||||||
|
errx(1, "No valid video mode was set");
|
||||||
|
if ( mode.control & DISPMSG_CONTROL_VGA )
|
||||||
|
errx(1, "A VGA text mode was set");
|
||||||
|
if ( mode.fb_format != 32 )
|
||||||
|
errx(1, "A 32-bit video mode wasn't set");
|
||||||
|
|
||||||
|
size_t framebuffer_length = mode.view_xres * mode.view_yres;
|
||||||
|
size_t framebuffer_size = sizeof(uint32_t) * framebuffer_length;
|
||||||
|
|
||||||
|
if ( display->fb_size != framebuffer_size )
|
||||||
|
{
|
||||||
|
display->fb.buffer = realloc(display->fb.buffer, framebuffer_size);
|
||||||
|
display->fb.xres = mode.view_xres;
|
||||||
|
display->fb.yres = mode.view_yres;
|
||||||
|
display->fb.pitch = mode.view_xres;
|
||||||
|
display->fb_size = framebuffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( display->wallpaper_size != framebuffer_size )
|
||||||
|
{
|
||||||
|
display->wallpaper.buffer =
|
||||||
|
realloc(display->wallpaper.buffer, framebuffer_size);
|
||||||
|
display->wallpaper.xres = mode.view_xres;
|
||||||
|
display->wallpaper.yres = mode.view_yres;
|
||||||
|
display->wallpaper.pitch = mode.view_xres;
|
||||||
|
display->wallpaper_size = framebuffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
display_on_resolution_change(display, mode.view_xres, mode.view_yres);
|
||||||
|
|
||||||
|
display_composit(display, display->fb);
|
||||||
|
|
||||||
|
struct dispmsg_write_memory write_memory_msg = {0};
|
||||||
|
write_memory_msg.msgid = DISPMSG_WRITE_MEMORY;
|
||||||
|
write_memory_msg.device = display->display.device;
|
||||||
|
write_memory_msg.size = framebuffer_size;
|
||||||
|
write_memory_msg.src = (uint8_t*) display->fb.buffer;
|
||||||
|
if ( dispmsg_issue(&write_memory_msg, sizeof(write_memory_msg)) != 0 )
|
||||||
|
err(1, "dispmsg_issue: dispmsg_write_memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_keyboard_event(struct display* display, uint32_t codepoint)
|
||||||
|
{
|
||||||
|
struct window* window = display->active_window;
|
||||||
|
|
||||||
|
int kbkey = KBKEY_DECODE(codepoint);
|
||||||
|
int abskbkey = kbkey < 0 ? -kbkey : kbkey;
|
||||||
|
|
||||||
|
if ( kbkey && (!window || !window->grab_input) )
|
||||||
|
{
|
||||||
|
switch ( abskbkey )
|
||||||
|
{
|
||||||
|
case KBKEY_LCTRL: display->key_lctrl = kbkey > 0; break;
|
||||||
|
case KBKEY_LALT: display->key_lalt = kbkey > 0; break;
|
||||||
|
case KBKEY_LSUPER: display->key_lsuper = kbkey > 0; break;
|
||||||
|
case KBKEY_RSUPER: display->key_rsuper = kbkey > 0; break;
|
||||||
|
}
|
||||||
|
if ( display->key_lctrl && display->key_lalt && kbkey == KBKEY_DELETE )
|
||||||
|
exit(0);
|
||||||
|
if ( display->key_lctrl && display->key_lalt && kbkey == KBKEY_T )
|
||||||
|
{
|
||||||
|
if ( !fork() )
|
||||||
|
{
|
||||||
|
execlp("terminal", "terminal", (char*) NULL);
|
||||||
|
_exit(127);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( display->key_lctrl && display->key_lalt && kbkey == -KBKEY_T )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( kbkey && window && !window->grab_input )
|
||||||
|
{
|
||||||
|
// TODO: Ctrl+Q when termninal has a way of handling it or not.
|
||||||
|
if ( (display->key_lalt && kbkey == KBKEY_F4) /* ||
|
||||||
|
(display->key_lctrl && kbkey == KBKEY_Q)*/ )
|
||||||
|
{
|
||||||
|
window_quit(window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( display->key_lalt && kbkey == KBKEY_F10 )
|
||||||
|
{
|
||||||
|
window_toggle_maximized(display->active_window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( display->key_lalt && kbkey == KBKEY_TAB )
|
||||||
|
{
|
||||||
|
if ( !display->tab_candidate )
|
||||||
|
display->tab_candidate = display->active_window;
|
||||||
|
struct window* old_candidate = display->tab_candidate;
|
||||||
|
if ( !(display->tab_candidate = old_candidate->below_window) )
|
||||||
|
display->tab_candidate = display->top_window;
|
||||||
|
window_render_frame(old_candidate);
|
||||||
|
window_render_frame(display->tab_candidate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( kbkey == -KBKEY_LALT && display->tab_candidate )
|
||||||
|
{
|
||||||
|
if ( display->tab_candidate != display->active_window )
|
||||||
|
display_set_active_window(display, display->tab_candidate);
|
||||||
|
display->tab_candidate = NULL;
|
||||||
|
window = display->active_window;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( display->key_lsuper || display->key_lsuper )
|
||||||
|
{
|
||||||
|
struct window* window = display->active_window;
|
||||||
|
if ( kbkey == KBKEY_LEFT )
|
||||||
|
{
|
||||||
|
window_tile_leftward(window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( kbkey == KBKEY_RIGHT )
|
||||||
|
{
|
||||||
|
window_tile_rightward(window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( kbkey == KBKEY_UP )
|
||||||
|
{
|
||||||
|
window_tile_up(window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( kbkey == KBKEY_DOWN )
|
||||||
|
{
|
||||||
|
window_tile_down(window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* grab_inputbed_string = " - Input Grabbed";
|
||||||
|
if ( kbkey == KBKEY_F11 && window && !window->grab_input )
|
||||||
|
{
|
||||||
|
// TODO: window->title can be null.
|
||||||
|
char* new_title;
|
||||||
|
if ( 0 <= asprintf(&new_title, "%s%s", window->title,
|
||||||
|
grab_inputbed_string) )
|
||||||
|
{
|
||||||
|
window->grab_input = true;
|
||||||
|
free(window->title);
|
||||||
|
window->title = new_title;
|
||||||
|
window_render_frame(window);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( kbkey == KBKEY_F12 && window && window->grab_input )
|
||||||
|
{
|
||||||
|
// TODO: Only remove from title if there.
|
||||||
|
size_t grab_inputbed_string_len = strlen(grab_inputbed_string);
|
||||||
|
window->title[strlen(window->title) - grab_inputbed_string_len] = '\0';
|
||||||
|
window->grab_input = false;
|
||||||
|
window_render_frame(window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !window )
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct event_keyboard event;
|
||||||
|
event.window_id = display->active_window->window_id;
|
||||||
|
event.codepoint = codepoint;
|
||||||
|
|
||||||
|
struct display_packet_header header;
|
||||||
|
header.id = EVENT_KEYBOARD;
|
||||||
|
header.size = sizeof(event);
|
||||||
|
|
||||||
|
assert(window->connection);
|
||||||
|
|
||||||
|
connection_schedule_transmit(window->connection, &header, sizeof(header));
|
||||||
|
connection_schedule_transmit(window->connection, &event, sizeof(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_mouse_event(struct display* display, uint8_t byte)
|
||||||
|
{
|
||||||
|
if ( display->mouse_byte_count == 0 && !(byte & MOUSE_ALWAYS_1) )
|
||||||
|
return;
|
||||||
|
if ( display->mouse_byte_count < MOUSE_PACKET_SIZE )
|
||||||
|
display->mouse_bytes[display->mouse_byte_count++] = byte;
|
||||||
|
if ( display->mouse_byte_count < MOUSE_PACKET_SIZE )
|
||||||
|
return;
|
||||||
|
display->mouse_byte_count = 0;
|
||||||
|
uint8_t* bytes = display->mouse_bytes;
|
||||||
|
|
||||||
|
int xm = MOUSE_X(bytes);
|
||||||
|
int ym = MOUSE_Y(bytes);
|
||||||
|
|
||||||
|
int old_pointer_x = display->pointer_x;
|
||||||
|
int old_pointer_y = display->pointer_y;
|
||||||
|
|
||||||
|
if ( xm*xm + ym*ym >= 2*2 )
|
||||||
|
{
|
||||||
|
xm *= 2;
|
||||||
|
ym *= 2;
|
||||||
|
}
|
||||||
|
else if ( xm*xm + ym*ym >= 5*5 )
|
||||||
|
{
|
||||||
|
xm *= 3;
|
||||||
|
ym *= 3;
|
||||||
|
}
|
||||||
|
display->pointer_x += xm;
|
||||||
|
display->pointer_y += ym;
|
||||||
|
if ( xm || ym )
|
||||||
|
display_schedule_redraw(display);
|
||||||
|
|
||||||
|
bool clipped_edge = false;
|
||||||
|
if ( display->pointer_x < 0 )
|
||||||
|
{
|
||||||
|
display->pointer_x = 0;
|
||||||
|
clipped_edge = true;
|
||||||
|
}
|
||||||
|
if ( display->pointer_y < 0 )
|
||||||
|
{
|
||||||
|
display->pointer_y = 0;
|
||||||
|
clipped_edge = true;
|
||||||
|
}
|
||||||
|
if ( display->screen_width < (size_t) display->pointer_x )
|
||||||
|
{
|
||||||
|
display->pointer_x = display->screen_width;
|
||||||
|
clipped_edge = true;
|
||||||
|
}
|
||||||
|
if ( display->screen_height < (size_t) display->pointer_y )
|
||||||
|
{
|
||||||
|
display->pointer_y = display->screen_height;
|
||||||
|
clipped_edge = true;
|
||||||
|
}
|
||||||
|
xm = display->pointer_x - old_pointer_x;
|
||||||
|
ym = display->pointer_y - old_pointer_y;
|
||||||
|
|
||||||
|
struct window* window;
|
||||||
|
for ( window = display->top_window; window; window = window->below_window )
|
||||||
|
{
|
||||||
|
if ( display->mouse_state != MOUSE_STATE_NONE )
|
||||||
|
break;
|
||||||
|
int grace = RESIZE_GRACE;
|
||||||
|
if ( window->window_state == WINDOW_STATE_MAXIMIZED )
|
||||||
|
grace = 0;
|
||||||
|
if ( old_pointer_x < window->left - grace )
|
||||||
|
continue;
|
||||||
|
if ( old_pointer_y < window->top - grace )
|
||||||
|
continue;
|
||||||
|
if ( old_pointer_x > window->left + (ssize_t) window->width + grace )
|
||||||
|
continue;
|
||||||
|
if ( old_pointer_y > window->top + (ssize_t) window->height + grace)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( !window )
|
||||||
|
return;
|
||||||
|
|
||||||
|
ssize_t window_pointer_x = display->pointer_x - window->left;
|
||||||
|
ssize_t window_pointer_y = display->pointer_y - window->top;
|
||||||
|
|
||||||
|
if ( display->active_window != window )
|
||||||
|
{
|
||||||
|
if ( bytes[0] & (MOUSE_BUTTON_LEFT | MOUSE_BUTTON_MIDDLE |
|
||||||
|
MOUSE_BUTTON_RIGHT) )
|
||||||
|
{
|
||||||
|
// TODO: Exit mouse from the current window.
|
||||||
|
display_set_active_window(display, window);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool maximized = window->window_state != WINDOW_STATE_REGULAR;
|
||||||
|
|
||||||
|
int b2 = 2;
|
||||||
|
int t0 = TITLE_HEIGHT;
|
||||||
|
|
||||||
|
size_t border_width = maximized ? 0 : b2 + 1;
|
||||||
|
size_t button_area_height = maximized ? t0 : t0 - (b2 + 1);
|
||||||
|
size_t button_area_width = button_area_height;
|
||||||
|
size_t button_area_top = maximized ? 0 : b2;
|
||||||
|
ssize_t buttons_x = window->width - border_width - button_area_width*3 + 1;
|
||||||
|
|
||||||
|
bool mouse_on_title = 0 <= window_pointer_x &&
|
||||||
|
window_pointer_x < (ssize_t) window->width &&
|
||||||
|
0 <= window_pointer_y &&
|
||||||
|
window_pointer_y <= (ssize_t) TITLE_HEIGHT;
|
||||||
|
|
||||||
|
for ( size_t n = 0; n < 3; n++ )
|
||||||
|
{
|
||||||
|
ssize_t bottom = button_area_top + button_area_height;
|
||||||
|
ssize_t left = button_area_width * n;
|
||||||
|
ssize_t right = button_area_width * (n + 1);
|
||||||
|
if ( (ssize_t) button_area_top <= window_pointer_y &&
|
||||||
|
window_pointer_y <= bottom &&
|
||||||
|
left <= window_pointer_x - buttons_x &&
|
||||||
|
window_pointer_x - buttons_x < right )
|
||||||
|
{
|
||||||
|
if ( display->mouse_state == MOUSE_STATE_NONE &&
|
||||||
|
(bytes[0] & MOUSE_BUTTON_LEFT) )
|
||||||
|
{
|
||||||
|
display->mouse_state = MOUSE_STATE_BUTTON_PRESS;
|
||||||
|
window->button_states[n] = BUTTON_STATE_PRESSED;
|
||||||
|
window_render_frame(window);
|
||||||
|
}
|
||||||
|
else if ( display->mouse_state == MOUSE_STATE_BUTTON_PRESS &&
|
||||||
|
window->button_states[n] == BUTTON_STATE_PRESSED &&
|
||||||
|
!(bytes[0] & MOUSE_BUTTON_LEFT) )
|
||||||
|
{
|
||||||
|
window->button_states[n] = BUTTON_STATE_NORMAL;
|
||||||
|
window_render_frame(window);
|
||||||
|
switch ( n )
|
||||||
|
{
|
||||||
|
case 0: /* TODO: Minimize. */ break;
|
||||||
|
case 1: window_toggle_maximized(window); break;
|
||||||
|
case 2: window_quit(window); break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( display->mouse_state == MOUSE_STATE_NONE &&
|
||||||
|
window->button_states[n] != BUTTON_STATE_HOVER )
|
||||||
|
{
|
||||||
|
window->button_states[n] = BUTTON_STATE_HOVER;
|
||||||
|
window_render_frame(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( window->button_states[n] != BUTTON_STATE_NORMAL )
|
||||||
|
{
|
||||||
|
window->button_states[n] = BUTTON_STATE_NORMAL;
|
||||||
|
if ( display->mouse_state != MOUSE_STATE_NONE )
|
||||||
|
display->mouse_state = MOUSE_STATE_IGNORE;
|
||||||
|
window_render_frame(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timespec double_click = timespec_make(0, 500000000);
|
||||||
|
|
||||||
|
if ( bytes[0] & MOUSE_BUTTON_LEFT )
|
||||||
|
{
|
||||||
|
if ( (display->mouse_state == MOUSE_STATE_NONE) )
|
||||||
|
{
|
||||||
|
// TODO: Stay in state until mouse release.
|
||||||
|
if ( display->key_lalt )
|
||||||
|
display->mouse_state = MOUSE_STATE_TITLE_MOVE;
|
||||||
|
else if ( mouse_on_title && window_pointer_x < buttons_x )
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
struct timespec elapsed =
|
||||||
|
timespec_sub(now, window->title_click_time);
|
||||||
|
if ( 0 <= window->title_click_time.tv_sec &&
|
||||||
|
timespec_le(elapsed, double_click) )
|
||||||
|
{
|
||||||
|
display->mouse_state = MOUSE_STATE_IGNORE;
|
||||||
|
window_toggle_maximized(window);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Reset this if clicked anywhere else or if the
|
||||||
|
// active window changes.
|
||||||
|
window->title_click_time = now;
|
||||||
|
display->mouse_state = MOUSE_STATE_TITLE_MOVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( window_pointer_x < 0 && window_pointer_y < 0 )
|
||||||
|
display->mouse_state = MOUSE_STATE_RESIZE_TOP_LEFT;
|
||||||
|
else if ( window_pointer_x < 0 &&
|
||||||
|
0 <= window_pointer_y &&
|
||||||
|
window_pointer_y < (ssize_t) window->height )
|
||||||
|
display->mouse_state = MOUSE_STATE_RESIZE_LEFT;
|
||||||
|
else if ( window_pointer_x < 0 &&
|
||||||
|
(ssize_t) window->height <= window_pointer_y )
|
||||||
|
display->mouse_state = MOUSE_STATE_RESIZE_BOTTOM_LEFT;
|
||||||
|
else if ( 0 <= window_pointer_x &&
|
||||||
|
window_pointer_x < (ssize_t) window->width &&
|
||||||
|
window_pointer_y < 0 )
|
||||||
|
display->mouse_state = MOUSE_STATE_RESIZE_TOP;
|
||||||
|
else if ( 0 <= window_pointer_x &&
|
||||||
|
window_pointer_x < (ssize_t) window->width &&
|
||||||
|
(ssize_t) window->height < window_pointer_y )
|
||||||
|
display->mouse_state = MOUSE_STATE_RESIZE_BOTTOM;
|
||||||
|
else if ( (ssize_t) window->width <= window_pointer_x &&
|
||||||
|
window_pointer_y < 0 )
|
||||||
|
display->mouse_state = MOUSE_STATE_RESIZE_TOP_RIGHT;
|
||||||
|
else if ( (ssize_t) window->width < window_pointer_x &&
|
||||||
|
0 <= window_pointer_y &&
|
||||||
|
window_pointer_y < (ssize_t) window->height )
|
||||||
|
display->mouse_state = MOUSE_STATE_RESIZE_RIGHT;
|
||||||
|
else if ( (ssize_t) window->width <= window_pointer_x &&
|
||||||
|
(ssize_t) window->height <= window_pointer_y )
|
||||||
|
display->mouse_state = MOUSE_STATE_RESIZE_BOTTOM_RIGHT;
|
||||||
|
if ( display->mouse_state != MOUSE_STATE_NONE &&
|
||||||
|
display->mouse_state != MOUSE_STATE_IGNORE )
|
||||||
|
display_schedule_redraw(display);
|
||||||
|
}
|
||||||
|
if ( xm || ym )
|
||||||
|
{
|
||||||
|
bool floating = window->window_state == WINDOW_STATE_REGULAR;
|
||||||
|
bool on_edge =
|
||||||
|
display->pointer_x == 0 ||
|
||||||
|
display->pointer_y == 0 ||
|
||||||
|
display->pointer_x == (ssize_t) display->screen_width ||
|
||||||
|
display->pointer_y == (ssize_t) display->screen_height;
|
||||||
|
switch ( display->mouse_state )
|
||||||
|
{
|
||||||
|
case MOUSE_STATE_NONE: break;
|
||||||
|
case MOUSE_STATE_IGNORE: break;
|
||||||
|
case MOUSE_STATE_BUTTON_PRESS: break;
|
||||||
|
case MOUSE_STATE_TITLE_MOVE:
|
||||||
|
if ( clipped_edge )
|
||||||
|
{
|
||||||
|
ssize_t x = display->pointer_x;
|
||||||
|
ssize_t y = display->pointer_y;
|
||||||
|
ssize_t sw = display->screen_width;
|
||||||
|
ssize_t sh = display->screen_height;
|
||||||
|
ssize_t corner_size = (sw < sh ? sw : sh) / 4;
|
||||||
|
if ( x < corner_size && y < corner_size )
|
||||||
|
window_tile_top_left(window);
|
||||||
|
else if ( sw - x < corner_size && y < corner_size )
|
||||||
|
window_tile_top_right(window);
|
||||||
|
else if ( x < corner_size && sh - y < corner_size )
|
||||||
|
window_tile_bottom_left(window);
|
||||||
|
else if ( sw - x < corner_size && sh - y < corner_size )
|
||||||
|
window_tile_bottom_right(window);
|
||||||
|
else if ( x == 0 )
|
||||||
|
window_tile_left(window);
|
||||||
|
else if ( x == sw )
|
||||||
|
window_tile_right(window);
|
||||||
|
else if ( y == 0 )
|
||||||
|
window_tile_top(window);
|
||||||
|
else if ( y == sh )
|
||||||
|
window_tile_bottom(window);
|
||||||
|
}
|
||||||
|
else if ( floating || !on_edge )
|
||||||
|
{
|
||||||
|
if ( !floating )
|
||||||
|
{
|
||||||
|
// The current behaviour of window_restore becomes
|
||||||
|
// awkward with tiling gestures. I could change the
|
||||||
|
// function itself, especially since this is currently
|
||||||
|
// its only callsite, but the old behaviour could be
|
||||||
|
// nice for a future untile hotkey. Thus, this hack.
|
||||||
|
window_restore(window);
|
||||||
|
window->top = display->pointer_y - TITLE_HEIGHT / 2;
|
||||||
|
window->left = display->pointer_x - window->width / 2;
|
||||||
|
}
|
||||||
|
window_move(window, window->left + xm, window->top + ym);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MOUSE_STATE_RESIZE_TOP_LEFT:
|
||||||
|
window_drag_resize(window, xm, ym, -xm, -ym);
|
||||||
|
break;
|
||||||
|
case MOUSE_STATE_RESIZE_LEFT:
|
||||||
|
window_drag_resize(window, xm, 0, -xm, 0);
|
||||||
|
break;
|
||||||
|
case MOUSE_STATE_RESIZE_BOTTOM_LEFT:
|
||||||
|
window_drag_resize(window, xm, 0, -xm, ym);
|
||||||
|
break;
|
||||||
|
case MOUSE_STATE_RESIZE_TOP:
|
||||||
|
window_drag_resize(window, 0, ym, 0, -ym);
|
||||||
|
break;
|
||||||
|
case MOUSE_STATE_RESIZE_BOTTOM:
|
||||||
|
window_drag_resize(window, 0, 0, 0, ym);
|
||||||
|
break;
|
||||||
|
case MOUSE_STATE_RESIZE_TOP_RIGHT:
|
||||||
|
window_drag_resize(window, 0, ym, xm, -ym);
|
||||||
|
break;
|
||||||
|
case MOUSE_STATE_RESIZE_RIGHT:
|
||||||
|
window_drag_resize(window, 0, 0, xm, 0);
|
||||||
|
break;
|
||||||
|
case MOUSE_STATE_RESIZE_BOTTOM_RIGHT:
|
||||||
|
window_drag_resize(window, 0, 0, xm, ym);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Leave mouse state if the top window closes.
|
||||||
|
// TODO: Leave mouse state if the top window is switched.
|
||||||
|
}
|
||||||
|
else if ( display->mouse_state != MOUSE_STATE_NONE )
|
||||||
|
{
|
||||||
|
display->mouse_state = MOUSE_STATE_NONE;
|
||||||
|
display_schedule_redraw(display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_on_resolution_change(struct display* display, size_t width,
|
||||||
|
size_t height)
|
||||||
|
{
|
||||||
|
if ( display->screen_width == width && display->screen_height == height )
|
||||||
|
return;
|
||||||
|
display->screen_width = width;
|
||||||
|
display->screen_height = height;
|
||||||
|
display->pointer_x = width / 2;
|
||||||
|
display->pointer_y = height / 2;
|
||||||
|
for ( struct window* window = display->bottom_window;
|
||||||
|
window;
|
||||||
|
window = window->above_window )
|
||||||
|
window_on_display_resolution_change(window, display);
|
||||||
|
wallpaper(display->wallpaper);
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
.Dd June 11, 2023
|
||||||
|
.Dt DISPLAY 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm display
|
||||||
|
.Nd desktop environment
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Ar session ...
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
is a desktop environment and windowing system compositor.
|
||||||
|
Applications talk to the
|
||||||
|
.Nm
|
||||||
|
server process to receive user input and show their graphical user interfaces
|
||||||
|
in windows.
|
||||||
|
.Pp
|
||||||
|
The user's preferred startup applications are launched on startup by launching
|
||||||
|
the
|
||||||
|
.Xr session
|
||||||
|
program (if set) or otherwise the
|
||||||
|
.Xr displayrc 5
|
||||||
|
script in the background.
|
||||||
|
.Pp
|
||||||
|
.Nm
|
||||||
|
exits when Control + Alt + Delete is pressed.
|
||||||
|
.Pp
|
||||||
|
The options are as follows:
|
||||||
|
.Bl -tag -width "12345678"
|
||||||
|
.It Fl m Ar mouse
|
||||||
|
Use
|
||||||
|
.Pa mouse
|
||||||
|
device instead of
|
||||||
|
.Pa /dev/mouse .
|
||||||
|
.It Fl t Ar tty
|
||||||
|
Use
|
||||||
|
.Pa tty
|
||||||
|
device instead of
|
||||||
|
.Pa /dev/tty .
|
||||||
|
.It Fl s Ar socket
|
||||||
|
Listen on
|
||||||
|
.Pa socket
|
||||||
|
instead of
|
||||||
|
.Pa /var/run/display .
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The keyboard shortcuts are as follows:
|
||||||
|
.Bl -tag -width "Control + Alt + Delete"
|
||||||
|
.It Alt + F4
|
||||||
|
Quit the current window.
|
||||||
|
.It Alt + F10
|
||||||
|
Maximize (or restore) the current window.
|
||||||
|
.It Alt + Tab
|
||||||
|
Switch to the next window.
|
||||||
|
.It Alt + Drag
|
||||||
|
Drag the current window.
|
||||||
|
.It Control + Alt + Delete
|
||||||
|
Exit the desktop environment.
|
||||||
|
.It Control + Alt + T
|
||||||
|
Launch the
|
||||||
|
.Xr terminal 1
|
||||||
|
application.
|
||||||
|
.It Super + Left
|
||||||
|
Tile the current window leftwards.
|
||||||
|
.It Super + Right
|
||||||
|
Tile the current window rightwards.
|
||||||
|
.It Super + Up
|
||||||
|
Tile the current window upwards.
|
||||||
|
.It Super + Down
|
||||||
|
Tile the current window downwards.
|
||||||
|
.It F11
|
||||||
|
Grab input for the current window.
|
||||||
|
.It F12
|
||||||
|
Release the input grab on the current window.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The mouse gestures are as follow:
|
||||||
|
.Bl -bullet
|
||||||
|
.It
|
||||||
|
Clicking on a window brings it to the foreground.
|
||||||
|
.It
|
||||||
|
Dragging the window title bar moves the window.
|
||||||
|
.It
|
||||||
|
Double clicking on the window title bar maximizes (or restores) the window.
|
||||||
|
.It
|
||||||
|
Clicking on the rectangle icon in the title bar maximizes (or restores) the
|
||||||
|
window.
|
||||||
|
.It
|
||||||
|
Clicking on the X icon in the title bar closes the window.
|
||||||
|
.It
|
||||||
|
Dragging the edges of a window resizes it.
|
||||||
|
.It
|
||||||
|
Windows can be tiled by moving them when the cursor meets the left, right, top,
|
||||||
|
and bottom edges or any corner.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The keyboard layout can be changed with the
|
||||||
|
.Xr chkblayout 1
|
||||||
|
program.
|
||||||
|
The display resolution can be changed with the
|
||||||
|
.Xr chvideomode 1
|
||||||
|
program.
|
||||||
|
.Sh ENVIRONMENT
|
||||||
|
.Bl -tag -width "DISPLAY_SOCKET"
|
||||||
|
.It Ev DISPLAY_SOCKET
|
||||||
|
.Nm
|
||||||
|
sets
|
||||||
|
.Ev DISPLAY_SOCKET
|
||||||
|
to the path of the
|
||||||
|
.Xr unix 4
|
||||||
|
socket where it listens for connections from applications.
|
||||||
|
Applications use
|
||||||
|
.Ev DISPLAY_SOCKET
|
||||||
|
to connect to
|
||||||
|
.Nm
|
||||||
|
or
|
||||||
|
.Pa /var/run/display
|
||||||
|
by default.
|
||||||
|
.El
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width 12345678 -compact
|
||||||
|
.It Pa ~/.displayrc , /etc/displayrc , /etc/default/displayrc
|
||||||
|
.Xr displayrc 5
|
||||||
|
script that spawns the user's preferred startup applications.
|
||||||
|
.It Pa /var/run/display
|
||||||
|
.Xr unix 4
|
||||||
|
socket where
|
||||||
|
.Nm
|
||||||
|
listens for connections from applications, as advertised in the
|
||||||
|
.Ev DISPLAY_SOCKET
|
||||||
|
environment variable.
|
||||||
|
.El
|
||||||
|
.Sh ASYNCHRONOUS EVENTS
|
||||||
|
.Bl -tag -width "SIGTERM"
|
||||||
|
.It Dv SIGTERM
|
||||||
|
Request daemon termination.
|
||||||
|
.El
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
.Nm
|
||||||
|
runs as a
|
||||||
|
.Xr daemon 7
|
||||||
|
until stopped by
|
||||||
|
.Dv SIGTERM ,
|
||||||
|
the user explicitly exits the desktop environment, or an application asks
|
||||||
|
it to exit.
|
||||||
|
.Nm
|
||||||
|
signals readiness on the
|
||||||
|
.Ev READYFD
|
||||||
|
file descriptor when the display server is ready to receive connections from
|
||||||
|
applications.
|
||||||
|
.Nm
|
||||||
|
will exit non-zero on any fatal startup error.
|
||||||
|
.Sh EXAMPLES
|
||||||
|
.Nm
|
||||||
|
can be selected as the user's graphical user interface with this executable
|
||||||
|
.Pa ~/.session
|
||||||
|
script:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
#!/bin/sh
|
||||||
|
exec display
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
.Xr chkblayout 1 ,
|
||||||
|
.Xr chvideomode 1 ,
|
||||||
|
.Xr display 1
|
||||||
|
will run the
|
||||||
|
.Xr displayrc 5
|
||||||
|
script on startup, which can be used to start applications.
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr terminal 1 ,
|
||||||
|
.Xr displayrc 5 ,
|
||||||
|
.Xr session 5
|
||||||
|
.Sh BUGS
|
||||||
|
The following features are not yet implemented:
|
||||||
|
.Bl -bullet -compact
|
||||||
|
.It
|
||||||
|
Windows cannot be minimized.
|
||||||
|
.It
|
||||||
|
Applications cannot receive mouse events.
|
||||||
|
.It
|
||||||
|
The wallpaper is random and cannot be controlled.
|
||||||
|
.El
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2017, 2022, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* display.c
|
||||||
|
* Display server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "arrow.inc"
|
||||||
|
|
||||||
|
#include "display.h"
|
||||||
|
#include "framebuffer.h"
|
||||||
|
#include "server.h"
|
||||||
|
|
||||||
|
uint32_t arrow_buffer[48 * 48];
|
||||||
|
struct framebuffer arrow_framebuffer = { 48, arrow_buffer, 48, 48 };
|
||||||
|
|
||||||
|
static void ready(void)
|
||||||
|
{
|
||||||
|
const char* readyfd_env = getenv("READYFD");
|
||||||
|
if ( !readyfd_env )
|
||||||
|
return;
|
||||||
|
int readyfd = atoi(readyfd_env);
|
||||||
|
char c = '\n';
|
||||||
|
write(readyfd, &c, 1);
|
||||||
|
close(readyfd);
|
||||||
|
unsetenv("READYFD");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
const char* mouse = "/dev/mouse";
|
||||||
|
const char* socket = "/var/run/display";
|
||||||
|
const char* tty = NULL;
|
||||||
|
|
||||||
|
int opt;
|
||||||
|
while ( (opt = getopt(argc, argv, "m:s:t:")) != -1 )
|
||||||
|
{
|
||||||
|
switch ( opt )
|
||||||
|
{
|
||||||
|
case 'm': mouse = optarg; break;
|
||||||
|
case 's': socket = optarg; break;
|
||||||
|
case 't': tty = optarg; break;
|
||||||
|
default: return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(arrow_buffer, arrow, sizeof(arrow));
|
||||||
|
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||||
|
|
||||||
|
if ( getpgid(0) != getpid() )
|
||||||
|
errx(1, "This program must be run in its own process group");
|
||||||
|
|
||||||
|
struct display display;
|
||||||
|
display_initialize(&display);
|
||||||
|
|
||||||
|
struct server server;
|
||||||
|
server_initialize(&server, &display, tty, mouse, socket);
|
||||||
|
|
||||||
|
if ( setenv("DISPLAY_SOCKET", server.server_path, 1) < 0 )
|
||||||
|
err(1, "setenv");
|
||||||
|
|
||||||
|
ready();
|
||||||
|
|
||||||
|
char* home_session = NULL;
|
||||||
|
char** session_argv = NULL;
|
||||||
|
if ( optind < argc )
|
||||||
|
session_argv = argv + optind;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char* home = getenv("HOME");
|
||||||
|
if ( home && asprintf(&home_session, "%s/.displayrc", home) < 0 )
|
||||||
|
err(1, "malloc");
|
||||||
|
const char* session_path = NULL;
|
||||||
|
if ( !access(home_session, F_OK) )
|
||||||
|
session_path = home_session;
|
||||||
|
else if ( !access("/etc/displayrc", F_OK) )
|
||||||
|
session_path = "/etc/displayrc";
|
||||||
|
else if ( !access("/etc/default/displayrc", F_OK) )
|
||||||
|
session_path = "/etc/default/displayrc";
|
||||||
|
if ( session_path )
|
||||||
|
session_argv = (char**) (const char*[]) {session_path, NULL};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( session_argv )
|
||||||
|
{
|
||||||
|
pid_t pid = fork();
|
||||||
|
if ( pid < 0 )
|
||||||
|
warn("fork");
|
||||||
|
else if ( pid == 0 )
|
||||||
|
{
|
||||||
|
execvp(session_argv[0], session_argv);
|
||||||
|
warn("%s", session_argv[0]);
|
||||||
|
_exit(127);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(home_session);
|
||||||
|
|
||||||
|
server_mainloop(&server);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2022, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* display.h
|
||||||
|
* Display server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DISPLAY_H
|
||||||
|
#define DISPLAY_H
|
||||||
|
|
||||||
|
#include <sys/display.h>
|
||||||
|
#include <sys/ps2mouse.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "damage-rect.h"
|
||||||
|
#include "framebuffer.h"
|
||||||
|
|
||||||
|
enum mouse_state
|
||||||
|
{
|
||||||
|
MOUSE_STATE_NONE,
|
||||||
|
MOUSE_STATE_IGNORE,
|
||||||
|
MOUSE_STATE_BUTTON_PRESS,
|
||||||
|
MOUSE_STATE_TITLE_MOVE,
|
||||||
|
MOUSE_STATE_RESIZE_BOTTOM,
|
||||||
|
MOUSE_STATE_RESIZE_BOTTOM_LEFT,
|
||||||
|
MOUSE_STATE_RESIZE_BOTTOM_RIGHT,
|
||||||
|
MOUSE_STATE_RESIZE_LEFT,
|
||||||
|
MOUSE_STATE_RESIZE_RIGHT,
|
||||||
|
MOUSE_STATE_RESIZE_TOP,
|
||||||
|
MOUSE_STATE_RESIZE_TOP_LEFT,
|
||||||
|
MOUSE_STATE_RESIZE_TOP_RIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct window;
|
||||||
|
|
||||||
|
struct display
|
||||||
|
{
|
||||||
|
struct tiocgdisplay display;
|
||||||
|
struct framebuffer fb;
|
||||||
|
struct framebuffer wallpaper;
|
||||||
|
size_t fb_size;
|
||||||
|
size_t wallpaper_size;
|
||||||
|
struct damage_rect damage_rect;
|
||||||
|
struct window* top_window;
|
||||||
|
struct window* bottom_window;
|
||||||
|
struct window* active_window;
|
||||||
|
struct window* tab_candidate;
|
||||||
|
size_t screen_width;
|
||||||
|
size_t screen_height;
|
||||||
|
size_t num_tabs;
|
||||||
|
bool key_lctrl;
|
||||||
|
bool key_lalt;
|
||||||
|
bool key_lsuper;
|
||||||
|
bool key_rsuper;
|
||||||
|
bool redraw;
|
||||||
|
int pointer_x;
|
||||||
|
int pointer_y;
|
||||||
|
enum mouse_state mouse_state;
|
||||||
|
size_t mouse_byte_count;
|
||||||
|
uint8_t mouse_bytes[MOUSE_PACKET_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
void display_initialize(struct display* display);
|
||||||
|
void assert_is_well_formed_display_list(struct display* display);
|
||||||
|
void assert_is_well_formed_display(struct display* display);
|
||||||
|
void display_link_window_at_top(struct display* display, struct window* window);
|
||||||
|
void display_unlink_window(struct display* display, struct window* window);
|
||||||
|
void display_unlink_window_removal(struct display* display,
|
||||||
|
struct window* window);
|
||||||
|
void display_unmark_active_window(struct display* display,
|
||||||
|
struct window* window);
|
||||||
|
void display_mark_active_window(struct display* display, struct window* window);
|
||||||
|
void display_update_active_window(struct display* display);
|
||||||
|
void display_move_window_to_top(struct display* display, struct window* window);
|
||||||
|
void display_change_active_window(struct display* display,
|
||||||
|
struct window* window);
|
||||||
|
void display_set_active_window(struct display* display, struct window* window);
|
||||||
|
void display_add_window(struct display* display, struct window* window);
|
||||||
|
void display_remove_window(struct display* display, struct window* window);
|
||||||
|
void display_composit(struct display* display, struct framebuffer fb);
|
||||||
|
void display_schedule_redraw(struct display* display);
|
||||||
|
void display_render(struct display* display);
|
||||||
|
void display_keyboard_event(struct display* display, uint32_t codepoint);
|
||||||
|
void display_on_resolution_change(struct display* display, size_t width,
|
||||||
|
size_t height);
|
||||||
|
void display_mouse_event(struct display* display, uint8_t byte);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,77 @@
|
||||||
|
.Dd June 11, 2023
|
||||||
|
.Dt DISPLAYRC 5
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm displayrc
|
||||||
|
.Nd startup graphical applications
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm ~/.displayrc
|
||||||
|
.Nm /etc/displayrc
|
||||||
|
.Nm /etc/default/displayrc
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Xr display 1
|
||||||
|
runs the
|
||||||
|
.Nm
|
||||||
|
script to launch the user's startup applications and prepare the desktop
|
||||||
|
environment according to the user's preferences.
|
||||||
|
.Pp
|
||||||
|
.Xr display 1
|
||||||
|
continues running after
|
||||||
|
.Nm
|
||||||
|
finishes running and any launched applications should be run as background
|
||||||
|
processes.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
script is found by searching for an executable script in the following paths:
|
||||||
|
.Bl -bullet -compact
|
||||||
|
.It
|
||||||
|
.Pa ~/.displayrc
|
||||||
|
.It
|
||||||
|
.Pa /etc/displayrc
|
||||||
|
.It
|
||||||
|
.Pa /etc/default/displayrc
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
.Nm
|
||||||
|
is not executed if no script is found.
|
||||||
|
.Sh ENVIRONMENT
|
||||||
|
.Nm
|
||||||
|
is executed with the following environment:
|
||||||
|
.Bl -tag -width "DISPLAY_SOCKET"
|
||||||
|
.It Ev DISPLAY_SOCKET
|
||||||
|
The path of the
|
||||||
|
.Xr unix 4 socket
|
||||||
|
where
|
||||||
|
.Xr display 1
|
||||||
|
is listening for connections from applications.
|
||||||
|
.El
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width "/etc/default/displayrc" -compact
|
||||||
|
.It Pa ~/.displayrc
|
||||||
|
The user's
|
||||||
|
.Nm
|
||||||
|
script.
|
||||||
|
.It Pa /etc/displayrc
|
||||||
|
The system administor provided
|
||||||
|
.Nm
|
||||||
|
script.
|
||||||
|
.It Pa /etc/default/displayrc
|
||||||
|
The operating system provided
|
||||||
|
.Nm
|
||||||
|
script.
|
||||||
|
.El
|
||||||
|
.Sh EXAMPLES
|
||||||
|
Launch a terminal with a text editor, another terminal with the user's default
|
||||||
|
shell, and launch the asteroids game.
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
terminal editor &
|
||||||
|
terminal &
|
||||||
|
asteroids &
|
||||||
|
.Ed
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr display 1 ,
|
||||||
|
.Xr terminal 1 ,
|
||||||
|
.Xr profile 5 ,
|
||||||
|
.Xr session 5 ,
|
||||||
|
.Xr shrc 5
|
|
@ -0,0 +1,281 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* server.c
|
||||||
|
* Display server main loop.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/termmode.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <display-protocol.h>
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
#include "display.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "vgafont.h"
|
||||||
|
|
||||||
|
static int open_local_server_socket(const char* path, int flags)
|
||||||
|
{
|
||||||
|
size_t path_length = strlen(path);
|
||||||
|
size_t addr_size = offsetof(struct sockaddr_un, sun_path) + path_length + 1;
|
||||||
|
struct sockaddr_un* sockaddr = malloc(addr_size);
|
||||||
|
if ( !sockaddr )
|
||||||
|
return -1;
|
||||||
|
sockaddr->sun_family = AF_LOCAL;
|
||||||
|
strcpy(sockaddr->sun_path, path);
|
||||||
|
int fd = socket(AF_LOCAL, SOCK_STREAM | flags, 0);
|
||||||
|
if ( fd < 0 )
|
||||||
|
return free(sockaddr), -1;
|
||||||
|
if ( bind(fd, (const struct sockaddr*) sockaddr, addr_size) < 0 )
|
||||||
|
return close(fd), free(sockaddr), -1;
|
||||||
|
if ( listen(fd, SOMAXCONN) < 0 )
|
||||||
|
return close(fd), free(sockaddr), -1;
|
||||||
|
free(sockaddr);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void server_initialize(struct server* server, struct display* display,
|
||||||
|
const char* tty, const char* mouse, const char* socket)
|
||||||
|
{
|
||||||
|
memset(server, 0, sizeof(*server));
|
||||||
|
|
||||||
|
server->display = display;
|
||||||
|
|
||||||
|
load_font();
|
||||||
|
|
||||||
|
server->tty_fd = 0;
|
||||||
|
if ( tty || !isatty(server->tty_fd) )
|
||||||
|
{
|
||||||
|
tty = tty ? tty : "/dev/tty";
|
||||||
|
server->tty_fd = open(tty, O_RDONLY);
|
||||||
|
if ( server->tty_fd < 0 )
|
||||||
|
err(1, tty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Support for multiple displays.
|
||||||
|
struct tiocgdisplays gdisplays = {0};
|
||||||
|
gdisplays.count = 1;
|
||||||
|
gdisplays.displays = &display->display;
|
||||||
|
if ( ioctl(server->tty_fd, TIOCGDISPLAYS, &gdisplays) < 0 ||
|
||||||
|
gdisplays.count == 0 )
|
||||||
|
errx(1, "%s: No video devices are associated with this terminal", tty);
|
||||||
|
|
||||||
|
server->mouse_fd = open(mouse, O_RDONLY | O_CLOEXEC);
|
||||||
|
if ( server->mouse_fd < 0 )
|
||||||
|
err(1, "%s", mouse);
|
||||||
|
|
||||||
|
server->server_path = socket;
|
||||||
|
server->server_fd = open_local_server_socket(server->server_path,
|
||||||
|
SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||||
|
if ( server->server_fd < 0 )
|
||||||
|
err(1, "open_local_server_socket: %s", server->server_path);
|
||||||
|
|
||||||
|
unsigned int termmode =
|
||||||
|
TERMMODE_KBKEY | TERMMODE_UNICODE | TERMMODE_NONBLOCK;
|
||||||
|
if ( settermmode(server->tty_fd, termmode) < 0 )
|
||||||
|
err(1, "settermmode");
|
||||||
|
|
||||||
|
server->pfds_count = server_pfds_count(server);
|
||||||
|
server->pfds =
|
||||||
|
reallocarray(NULL, sizeof(struct pollfd), server->pfds_count);
|
||||||
|
if ( !server->pfds )
|
||||||
|
err(1, "malloc");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool server_accept(struct server* server)
|
||||||
|
{
|
||||||
|
int client_fd = accept4(server->server_fd, NULL, NULL, SOCK_NONBLOCK);
|
||||||
|
if ( client_fd < 0 )
|
||||||
|
{
|
||||||
|
warn("accept: %s", server->server_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( server->connections_used == server->connections_length )
|
||||||
|
{
|
||||||
|
size_t new_length = server->connections_length * 2;
|
||||||
|
if ( !new_length )
|
||||||
|
new_length = 16;
|
||||||
|
struct connection** new_connections =
|
||||||
|
reallocarray(server->connections, new_length,
|
||||||
|
sizeof(struct connection*));
|
||||||
|
if ( !new_connections )
|
||||||
|
{
|
||||||
|
warn("dropped connection: %s: malloc", server->server_path);
|
||||||
|
close(client_fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
server->connections = new_connections;
|
||||||
|
server->connections_length = new_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t new_pfds_count = server_pfds_count(server) + 1;
|
||||||
|
struct pollfd* new_pfds =
|
||||||
|
reallocarray(server->pfds, sizeof(struct pollfd), new_pfds_count);
|
||||||
|
if ( !new_pfds )
|
||||||
|
{
|
||||||
|
warn("dropped connection: %s: malloc", server->server_path);
|
||||||
|
close(client_fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
server->pfds = new_pfds;
|
||||||
|
server->pfds_count = new_pfds_count;
|
||||||
|
|
||||||
|
struct connection* connection = malloc(sizeof(struct connection));
|
||||||
|
if ( !connection )
|
||||||
|
{
|
||||||
|
warn("dropped connection: %s: malloc", server->server_path);
|
||||||
|
close(client_fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
server->connections[server->connections_used++] = connection;
|
||||||
|
connection_initialize(connection, server->display, client_fd);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t server_pfds_count(const struct server* server)
|
||||||
|
{
|
||||||
|
return 3 + server->connections_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
void server_poll(struct server* server)
|
||||||
|
{
|
||||||
|
int code;
|
||||||
|
while ( 0 < waitpid(-1, &code, WNOHANG) )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pollfd* pfds = server->pfds;
|
||||||
|
|
||||||
|
pfds[0].fd = server->server_fd;
|
||||||
|
pfds[0].events = POLLIN;
|
||||||
|
pfds[0].revents = 0;
|
||||||
|
pfds[1].fd = server->tty_fd;
|
||||||
|
pfds[1].events = POLLIN;
|
||||||
|
pfds[1].revents = 0;
|
||||||
|
pfds[2].fd = server->mouse_fd;
|
||||||
|
pfds[2].events = POLLIN;
|
||||||
|
pfds[2].revents = 0;
|
||||||
|
size_t cpfd_off = 3;
|
||||||
|
|
||||||
|
size_t connections_polled = server->connections_used;
|
||||||
|
for ( size_t i = 0; i < connections_polled; i++ )
|
||||||
|
{
|
||||||
|
struct pollfd* pfd = &pfds[cpfd_off + i];
|
||||||
|
struct connection* connection = server->connections[i];
|
||||||
|
pfd->fd = connection->fd;
|
||||||
|
pfd->events = connection_interested_poll_events(connection);
|
||||||
|
pfd->revents = 0;
|
||||||
|
}
|
||||||
|
size_t pfds_used = cpfd_off + connections_polled;
|
||||||
|
|
||||||
|
int num_events = ppoll(pfds, pfds_used, NULL, NULL);
|
||||||
|
if ( num_events < 0 )
|
||||||
|
err(1, "poll");
|
||||||
|
|
||||||
|
if ( pfds[0].revents )
|
||||||
|
{
|
||||||
|
// TODO: Handle if this can actually happen.
|
||||||
|
assert(!(pfds[0].revents & POLLERR));
|
||||||
|
assert(!(pfds[0].revents & POLLHUP));
|
||||||
|
assert(!(pfds[0].revents & POLLNVAL));
|
||||||
|
|
||||||
|
server_accept(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pfds[1].revents )
|
||||||
|
{
|
||||||
|
// TODO: Handle if this can actually happen.
|
||||||
|
assert(!(pfds[1].revents & POLLERR));
|
||||||
|
assert(!(pfds[1].revents & POLLHUP));
|
||||||
|
assert(!(pfds[1].revents & POLLNVAL));
|
||||||
|
|
||||||
|
uint32_t codepoint;
|
||||||
|
ssize_t size = sizeof(codepoint);
|
||||||
|
while ( read(server->tty_fd, &codepoint, size) == size )
|
||||||
|
display_keyboard_event(server->display, codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pfds[2].revents )
|
||||||
|
{
|
||||||
|
// TODO: Handle if this can actually happen.
|
||||||
|
assert(!(pfds[2].revents & POLLERR));
|
||||||
|
assert(!(pfds[2].revents & POLLHUP));
|
||||||
|
assert(!(pfds[2].revents & POLLNVAL));
|
||||||
|
|
||||||
|
unsigned char events[64];
|
||||||
|
ssize_t amount = read(server->mouse_fd, events, sizeof(events));
|
||||||
|
for ( ssize_t i = 0; i < amount; i++ )
|
||||||
|
display_mouse_event(server->display, events[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool any_disconnect = false;
|
||||||
|
for ( size_t i = 0; i < connections_polled; i++ )
|
||||||
|
{
|
||||||
|
struct pollfd* pfd = &pfds[cpfd_off + i];
|
||||||
|
if ( !pfd->revents )
|
||||||
|
continue;
|
||||||
|
struct connection* connection = server->connections[i];
|
||||||
|
if ( pfd->revents & (POLLERR | POLLHUP | POLLNVAL) &&
|
||||||
|
!(pfd->revents & POLLIN) )
|
||||||
|
{
|
||||||
|
connection_destroy(connection);
|
||||||
|
free(connection);
|
||||||
|
server->connections[i] = NULL;
|
||||||
|
any_disconnect = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( pfd->revents & POLLOUT )
|
||||||
|
connection_can_write(connection);
|
||||||
|
if ( pfd->revents & POLLIN )
|
||||||
|
connection_can_read(connection, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact the array down here so the pfds match the connections above.
|
||||||
|
if ( any_disconnect )
|
||||||
|
{
|
||||||
|
size_t new_used = 0;
|
||||||
|
for ( size_t i = 0; i < server->connections_used; i++ )
|
||||||
|
{
|
||||||
|
if ( server->connections[i] )
|
||||||
|
server->connections[new_used++] = server->connections[i];
|
||||||
|
}
|
||||||
|
server->connections_used = new_used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void server_mainloop(struct server* server)
|
||||||
|
{
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
display_render(server->display);
|
||||||
|
server_poll(server);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* connection.h
|
||||||
|
* Display server main loop.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SERVER_H
|
||||||
|
#define SERVER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct connection;
|
||||||
|
struct display;
|
||||||
|
struct pollfd;
|
||||||
|
|
||||||
|
struct server
|
||||||
|
{
|
||||||
|
struct display* display;
|
||||||
|
const char* server_path;
|
||||||
|
int server_fd;
|
||||||
|
int tty_fd;
|
||||||
|
int mouse_fd;
|
||||||
|
struct connection** connections;
|
||||||
|
size_t connections_used;
|
||||||
|
size_t connections_length;
|
||||||
|
struct pollfd* pfds;
|
||||||
|
size_t pfds_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
void server_initialize(struct server* server, struct display* display,
|
||||||
|
const char* tty, const char* mouse, const char* socket);
|
||||||
|
bool server_accept(struct server* server);
|
||||||
|
size_t server_pfds_count(const struct server* server);
|
||||||
|
void server_poll(struct server* server);
|
||||||
|
void server_mainloop(struct server* server);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,554 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2017, 2022, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* window.c
|
||||||
|
* Window abstraction.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <timespec.h>
|
||||||
|
|
||||||
|
#include <display-protocol.h>
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
#include "display.h"
|
||||||
|
#include "framebuffer.h"
|
||||||
|
#include "pixel.h"
|
||||||
|
#include "vgafont.h"
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
struct framebuffer window_client_buffer(struct window* window)
|
||||||
|
{
|
||||||
|
if ( window->window_state != WINDOW_STATE_REGULAR )
|
||||||
|
return framebuffer_crop(window->buffer, 0, TITLE_HEIGHT,
|
||||||
|
window->width, window->height - TITLE_HEIGHT);
|
||||||
|
return framebuffer_crop(window->buffer, BORDER_WIDTH, TITLE_HEIGHT,
|
||||||
|
window->width - 2 * BORDER_WIDTH,
|
||||||
|
window->height - TITLE_HEIGHT - BORDER_WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_schedule_redraw(struct window* window)
|
||||||
|
{
|
||||||
|
if ( window->show )
|
||||||
|
display_schedule_redraw(window->display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_render_frame(struct window* window)
|
||||||
|
{
|
||||||
|
if ( !window->width || !window->height )
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool has_focus = window->display->tab_candidate ?
|
||||||
|
window->display->tab_candidate == window :
|
||||||
|
window->display->active_window == window;
|
||||||
|
|
||||||
|
uint32_t glass_color = has_focus ? make_color_a(200, 200, 255, 192)
|
||||||
|
: make_color_a(180, 180, 255, 128);
|
||||||
|
uint32_t title_color = has_focus ? make_color_a(16, 16, 16, 240)
|
||||||
|
: make_color_a(32, 32, 32, 200);
|
||||||
|
uint32_t button_hover_glass = make_color_a(220, 220, 255, 255);
|
||||||
|
uint32_t button_press_glass = make_color_a(180, 180, 255, 255);
|
||||||
|
|
||||||
|
size_t start_x = 0;
|
||||||
|
size_t start_y = 0;
|
||||||
|
size_t end_x = window->width - 1;
|
||||||
|
size_t end_y = window->height - 1;
|
||||||
|
|
||||||
|
bool maximized = window->window_state != WINDOW_STATE_REGULAR;
|
||||||
|
|
||||||
|
int b0 = 0;
|
||||||
|
int b1 = 1;
|
||||||
|
int b2 = 2;
|
||||||
|
int b3 = BORDER_WIDTH;
|
||||||
|
int t0 = TITLE_HEIGHT;
|
||||||
|
|
||||||
|
for ( size_t y = start_y; y <= end_y; y++ )
|
||||||
|
{
|
||||||
|
for ( size_t x = start_x; x <= end_x; x++ )
|
||||||
|
{
|
||||||
|
uint32_t color;
|
||||||
|
if ( maximized && y < start_y + t0 )
|
||||||
|
color = glass_color;
|
||||||
|
else if ( maximized )
|
||||||
|
continue;
|
||||||
|
else if ( x == start_x + b0 || x == end_x - b0 ||
|
||||||
|
y == start_y + b0 || y == end_y - b0 )
|
||||||
|
color = make_color_a(0, 0, 0, 32);
|
||||||
|
else if ( x == start_x + b1 || x == end_x - b1 ||
|
||||||
|
y == start_y + b1 || y == end_y - b1 )
|
||||||
|
color = make_color_a(0, 0, 0, 64);
|
||||||
|
else if ( x == start_x + b2 || x == end_x - b2 ||
|
||||||
|
y == start_y + b2 || y == end_y - b2 )
|
||||||
|
color = make_color(240, 240, 250);
|
||||||
|
else if ( x < start_x + (b3-1) || x > end_x - (b3-1) ||
|
||||||
|
y < start_y + (t0-1) || y > end_y - (b3-1) )
|
||||||
|
color = glass_color;
|
||||||
|
else if ( x == start_x + (b3-1) || x == end_x - (b3-1) ||
|
||||||
|
y == start_y + (t0-1) || y == end_y - (b3-1) )
|
||||||
|
color = make_color(64, 64, 64);
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
framebuffer_set_pixel(window->buffer, x, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* tt = window->title ? window->title : "";
|
||||||
|
ssize_t tt_width = render_text_width(tt); // Potentially adjusted later.
|
||||||
|
size_t tt_height = FONT_HEIGHT;
|
||||||
|
size_t tt_pos_y = (TITLE_HEIGHT - FONT_HEIGHT) / 2 + 2;
|
||||||
|
uint32_t tt_color = title_color;
|
||||||
|
|
||||||
|
size_t border_width = maximized ? 0 : b2 + 1;
|
||||||
|
size_t button_area_height = maximized ? t0 : t0 - (b2 + 1);
|
||||||
|
size_t button_area_width = button_area_height;
|
||||||
|
size_t button_area_top = maximized ? 0 : b2;
|
||||||
|
size_t button_size = FONT_WIDTH - 1;
|
||||||
|
size_t button_top = (button_area_height - button_size + 1) / 2;
|
||||||
|
size_t button_left = (button_area_width - button_size + 1) / 2;
|
||||||
|
ssize_t buttons_x = window->width - border_width - button_area_width*3 + 1;
|
||||||
|
struct framebuffer buttons_fb =
|
||||||
|
framebuffer_crop(window->buffer, buttons_x, button_area_top,
|
||||||
|
button_area_width * 3, button_area_height);
|
||||||
|
for ( size_t n = 0; n < 3; n++ )
|
||||||
|
{
|
||||||
|
uint32_t color = glass_color;
|
||||||
|
switch ( window->button_states[n] )
|
||||||
|
{
|
||||||
|
case BUTTON_STATE_NORMAL: continue;
|
||||||
|
case BUTTON_STATE_HOVER: color = button_hover_glass; break;
|
||||||
|
case BUTTON_STATE_PRESSED: color = button_press_glass; break;
|
||||||
|
}
|
||||||
|
size_t bx = button_area_width * n;
|
||||||
|
size_t by = 0;
|
||||||
|
for ( size_t y = 0; y < button_area_height; y++ )
|
||||||
|
for ( size_t x = 0; x < button_area_width; x++ )
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + x, by + y, color);
|
||||||
|
}
|
||||||
|
for ( size_t i = 0; i < button_size; i++ )
|
||||||
|
{
|
||||||
|
size_t bx = button_area_width * 0 + button_left;
|
||||||
|
size_t by = button_top;
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + i,
|
||||||
|
by + button_size - 1, tt_color);
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + i,
|
||||||
|
by + button_size - 2, tt_color);
|
||||||
|
}
|
||||||
|
for ( size_t i = 0; i < button_size; i++ )
|
||||||
|
{
|
||||||
|
size_t bx = button_area_width * 1 + button_left;
|
||||||
|
size_t by = button_top;
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + i,
|
||||||
|
by, tt_color);
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + i,
|
||||||
|
by + button_size - 1 , tt_color);
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx,
|
||||||
|
by + i, tt_color);
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + button_size - 1,
|
||||||
|
by + i, tt_color);
|
||||||
|
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + i,
|
||||||
|
by + 1, tt_color);
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + i,
|
||||||
|
by + button_size - 2 , tt_color);
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + 1,
|
||||||
|
by + i, tt_color);
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + button_size - 2,
|
||||||
|
by + i, tt_color);
|
||||||
|
}
|
||||||
|
for ( size_t i = 0; i < button_size; i++ )
|
||||||
|
{
|
||||||
|
size_t bx = button_area_width * 2 + button_left;
|
||||||
|
size_t by = button_top;
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + i,
|
||||||
|
by + i, tt_color);
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + i,
|
||||||
|
by + button_size - 1 - i, tt_color);
|
||||||
|
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + i - 1,
|
||||||
|
by + i, tt_color);
|
||||||
|
framebuffer_set_pixel(buttons_fb, bx + i - 1,
|
||||||
|
by + button_size - 1 - i, tt_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t q = 500 - window->width;
|
||||||
|
ssize_t q_width = 200;
|
||||||
|
q = q < q_width ? q : q_width;
|
||||||
|
q = 0 < q ? q : 0;
|
||||||
|
ssize_t center_over = window->width - (button_area_width * 3 * q / q_width);
|
||||||
|
ssize_t tt_pos_x = (center_over - tt_width) / 2;
|
||||||
|
if ( tt_pos_x < (ssize_t)border_width )
|
||||||
|
{
|
||||||
|
tt_pos_x = border_width;
|
||||||
|
tt_width = buttons_x - border_width;
|
||||||
|
tt_width = 0 < tt_width ? tt_width : 0;
|
||||||
|
}
|
||||||
|
render_text(framebuffer_crop(window->buffer, tt_pos_x, tt_pos_y,
|
||||||
|
tt_width, tt_height), tt, tt_color);
|
||||||
|
window_schedule_redraw(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_move(struct window* window, size_t left, size_t top)
|
||||||
|
{
|
||||||
|
window->left = left;
|
||||||
|
window->top = top;
|
||||||
|
window_schedule_redraw(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_client_resize(struct window* window,
|
||||||
|
size_t client_width,
|
||||||
|
size_t client_height)
|
||||||
|
{
|
||||||
|
if ( window->window_state != WINDOW_STATE_MINIMIZED )
|
||||||
|
window->window_state = WINDOW_STATE_REGULAR;
|
||||||
|
|
||||||
|
struct framebuffer old_fb = window->buffer;
|
||||||
|
|
||||||
|
window->width = client_width + BORDER_WIDTH + BORDER_WIDTH;
|
||||||
|
window->height = client_height + TITLE_HEIGHT + BORDER_WIDTH;
|
||||||
|
|
||||||
|
window->buffer.xres = window->width;
|
||||||
|
window->buffer.yres = window->height;
|
||||||
|
window->buffer.pitch = window->width;
|
||||||
|
// TODO: Check malloc.
|
||||||
|
window->buffer.buffer =
|
||||||
|
malloc(sizeof(uint32_t) * window->width * window->height);
|
||||||
|
for ( size_t y = 0; y < window->height; y++ )
|
||||||
|
for ( size_t x = 0; x < window->width; x++ )
|
||||||
|
framebuffer_set_pixel(window->buffer, x, y,
|
||||||
|
framebuffer_get_pixel(old_fb, x, y));
|
||||||
|
|
||||||
|
free(old_fb.buffer);
|
||||||
|
|
||||||
|
window_render_frame(window);
|
||||||
|
window_notify_client_resize(window);
|
||||||
|
window_schedule_redraw(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_resize(struct window* window, size_t width, size_t height)
|
||||||
|
{
|
||||||
|
if ( width < BORDER_WIDTH + BORDER_WIDTH )
|
||||||
|
width = BORDER_WIDTH + BORDER_WIDTH;
|
||||||
|
if ( height < TITLE_HEIGHT + BORDER_WIDTH )
|
||||||
|
height = TITLE_HEIGHT + BORDER_WIDTH;
|
||||||
|
// TODO: Keep proper track of this for each state.
|
||||||
|
size_t client_width = width - (BORDER_WIDTH + BORDER_WIDTH);
|
||||||
|
size_t client_height = height - (TITLE_HEIGHT + BORDER_WIDTH);
|
||||||
|
window_client_resize(window, client_width, client_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_drag_resize(struct window* window, int ld, int td, int wd, int hd)
|
||||||
|
{
|
||||||
|
// TODO: Keep proper track of this for each state.
|
||||||
|
size_t client_width = window->width - (BORDER_WIDTH + BORDER_WIDTH);
|
||||||
|
size_t client_height = window->height - (TITLE_HEIGHT + BORDER_WIDTH);
|
||||||
|
if ( ld || td )
|
||||||
|
window_move(window, window->left + ld, window->top + td);
|
||||||
|
if ( wd || hd )
|
||||||
|
{
|
||||||
|
ssize_t new_width = (ssize_t) client_width + wd;
|
||||||
|
ssize_t new_height = (ssize_t) client_height + hd;
|
||||||
|
if ( new_width < 1 )
|
||||||
|
new_width = 1;
|
||||||
|
if ( new_height < 1 )
|
||||||
|
new_height = 1;
|
||||||
|
window_client_resize(window, new_width, new_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t next_window_position = 25;
|
||||||
|
|
||||||
|
void window_initialize(struct window* window,
|
||||||
|
struct connection* connection,
|
||||||
|
struct display* display,
|
||||||
|
uint32_t window_id)
|
||||||
|
{
|
||||||
|
memset(window, 0, sizeof(*window));
|
||||||
|
window->created = true;
|
||||||
|
window->connection = connection;
|
||||||
|
window->display = display;
|
||||||
|
window->title_click_time = timespec_make(-1, 0);
|
||||||
|
window->window_id = window_id;
|
||||||
|
display_add_window(window->display, window);
|
||||||
|
window->top = next_window_position;
|
||||||
|
window->left = next_window_position;
|
||||||
|
size_t max_position = display->screen_width < display->screen_height ?
|
||||||
|
display->screen_width : display->screen_height;
|
||||||
|
max_position = (max_position * 6) / 10;
|
||||||
|
next_window_position += 30;
|
||||||
|
next_window_position %= max_position;
|
||||||
|
window_client_resize(window, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_quit(struct window* window)
|
||||||
|
{
|
||||||
|
struct event_quit event;
|
||||||
|
event.window_id = window->window_id;
|
||||||
|
|
||||||
|
struct display_packet_header header;
|
||||||
|
header.id = EVENT_QUIT;
|
||||||
|
header.size = sizeof(event);
|
||||||
|
|
||||||
|
assert(window->connection);
|
||||||
|
|
||||||
|
connection_schedule_transmit(window->connection, &header, sizeof(header));
|
||||||
|
connection_schedule_transmit(window->connection, &event, sizeof(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_destroy(struct window* window)
|
||||||
|
{
|
||||||
|
display_remove_window(window->display, window);
|
||||||
|
free(window->buffer.buffer);
|
||||||
|
free(window->title);
|
||||||
|
memset(window, 0, sizeof(*window));
|
||||||
|
window->created = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_on_display_resolution_change(struct window* window,
|
||||||
|
struct display* display)
|
||||||
|
{
|
||||||
|
// TODO: Move window back inside screen.
|
||||||
|
if ( window->window_state == WINDOW_STATE_MAXIMIZED )
|
||||||
|
{
|
||||||
|
// TODO: Change size of maximized window.
|
||||||
|
(void) display;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile(struct window* window, enum window_state state, size_t left,
|
||||||
|
size_t top, size_t width, size_t height)
|
||||||
|
{
|
||||||
|
if ( window->window_state == state )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( window->window_state == WINDOW_STATE_REGULAR )
|
||||||
|
{
|
||||||
|
window->saved_left = window->left;
|
||||||
|
window->saved_top = window->top;
|
||||||
|
window->saved_width = window->width;
|
||||||
|
window->saved_height = window->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(window->buffer.buffer);
|
||||||
|
|
||||||
|
window->left = left;
|
||||||
|
window->top = top;
|
||||||
|
window->width = width;
|
||||||
|
window->height = height;
|
||||||
|
|
||||||
|
// TODO: Share logic with window_client_resize.
|
||||||
|
window->buffer.xres = window->width;
|
||||||
|
window->buffer.yres = window->height;
|
||||||
|
window->buffer.pitch = window->width;
|
||||||
|
// TODO: Check malloc.
|
||||||
|
window->buffer.buffer =
|
||||||
|
calloc(1, sizeof(uint32_t) * window->width * window->height);
|
||||||
|
|
||||||
|
window->window_state = state;
|
||||||
|
|
||||||
|
window_render_frame(window);
|
||||||
|
window_notify_client_resize(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_maximize(struct window* window)
|
||||||
|
{
|
||||||
|
window_tile(window, WINDOW_STATE_MAXIMIZED,
|
||||||
|
0, 0,
|
||||||
|
window->display->screen_width, window->display->screen_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_restore(struct window* window)
|
||||||
|
{
|
||||||
|
if ( window->window_state == WINDOW_STATE_REGULAR )
|
||||||
|
return;
|
||||||
|
window->top = window->saved_top;
|
||||||
|
window->left = window->saved_left;
|
||||||
|
window_client_resize(window, window->saved_width - 2 * BORDER_WIDTH,
|
||||||
|
window->saved_height - TITLE_HEIGHT - BORDER_WIDTH);
|
||||||
|
window_notify_client_resize(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_toggle_maximized(struct window* window)
|
||||||
|
{
|
||||||
|
if ( window->window_state == WINDOW_STATE_MAXIMIZED )
|
||||||
|
window_restore(window);
|
||||||
|
else
|
||||||
|
window_maximize(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_leftward(struct window* window)
|
||||||
|
{
|
||||||
|
switch ( window->window_state )
|
||||||
|
{
|
||||||
|
case WINDOW_STATE_REGULAR: window_tile_left(window); break;
|
||||||
|
case WINDOW_STATE_MAXIMIZED: window_tile_left(window); break;
|
||||||
|
case WINDOW_STATE_MINIMIZED: window_tile_right(window); break;
|
||||||
|
case WINDOW_STATE_TILE_LEFT: break;
|
||||||
|
case WINDOW_STATE_TILE_RIGHT: window_restore(window); break;
|
||||||
|
case WINDOW_STATE_TILE_TOP: window_tile_top_left(window); break;
|
||||||
|
case WINDOW_STATE_TILE_TOP_LEFT: break;
|
||||||
|
case WINDOW_STATE_TILE_TOP_RIGHT: window_tile_top(window); break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM: window_tile_bottom_left(window); break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM_LEFT: break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM_RIGHT: window_tile_bottom(window); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_rightward(struct window* window)
|
||||||
|
{
|
||||||
|
switch ( window->window_state )
|
||||||
|
{
|
||||||
|
case WINDOW_STATE_REGULAR: window_tile_right(window); break;
|
||||||
|
case WINDOW_STATE_MAXIMIZED: window_tile_right(window); break;
|
||||||
|
case WINDOW_STATE_MINIMIZED: window_tile_right(window); break;
|
||||||
|
case WINDOW_STATE_TILE_LEFT: window_restore(window); break;
|
||||||
|
case WINDOW_STATE_TILE_RIGHT: break;
|
||||||
|
case WINDOW_STATE_TILE_TOP: window_tile_top_right(window); break;
|
||||||
|
case WINDOW_STATE_TILE_TOP_LEFT: window_tile_top(window); break;
|
||||||
|
case WINDOW_STATE_TILE_TOP_RIGHT: break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM: window_tile_bottom_right(window); break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM_LEFT: window_tile_bottom(window); break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM_RIGHT: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_up(struct window* window)
|
||||||
|
{
|
||||||
|
switch ( window->window_state )
|
||||||
|
{
|
||||||
|
case WINDOW_STATE_REGULAR: window_tile_top(window); break;
|
||||||
|
case WINDOW_STATE_MAXIMIZED: window_restore(window); break;
|
||||||
|
case WINDOW_STATE_MINIMIZED: window_tile_top(window); break;
|
||||||
|
case WINDOW_STATE_TILE_LEFT: window_tile_top_left(window); break;
|
||||||
|
case WINDOW_STATE_TILE_RIGHT: window_tile_top_right(window); break;
|
||||||
|
case WINDOW_STATE_TILE_TOP: window_maximize(window); break;
|
||||||
|
case WINDOW_STATE_TILE_TOP_LEFT: break;
|
||||||
|
case WINDOW_STATE_TILE_TOP_RIGHT: break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM: window_restore(window); break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM_LEFT: window_tile_left(window); break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM_RIGHT: window_tile_right(window); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_down(struct window* window)
|
||||||
|
{
|
||||||
|
switch ( window->window_state )
|
||||||
|
{
|
||||||
|
case WINDOW_STATE_REGULAR: window_tile_bottom(window); break;
|
||||||
|
case WINDOW_STATE_MAXIMIZED: window_tile_top(window); break;
|
||||||
|
case WINDOW_STATE_MINIMIZED: window_tile_bottom(window); break;
|
||||||
|
case WINDOW_STATE_TILE_LEFT: window_tile_bottom_left(window); break;
|
||||||
|
case WINDOW_STATE_TILE_RIGHT: window_tile_bottom_right(window); break;
|
||||||
|
case WINDOW_STATE_TILE_TOP: window_restore(window); break;
|
||||||
|
case WINDOW_STATE_TILE_TOP_LEFT: window_tile_left(window); break;
|
||||||
|
case WINDOW_STATE_TILE_TOP_RIGHT: window_tile_right(window); break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM: break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM_LEFT: break;
|
||||||
|
case WINDOW_STATE_TILE_BOTTOM_RIGHT: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_left(struct window* window)
|
||||||
|
{
|
||||||
|
window_tile(window, WINDOW_STATE_TILE_LEFT,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
window->display->screen_width / 2,
|
||||||
|
window->display->screen_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_right(struct window* window)
|
||||||
|
{
|
||||||
|
window_tile(window, WINDOW_STATE_TILE_RIGHT,
|
||||||
|
(window->display->screen_width + 1) / 2,
|
||||||
|
0,
|
||||||
|
(window->display->screen_width + 1) / 2,
|
||||||
|
window->display->screen_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_top(struct window* window)
|
||||||
|
{
|
||||||
|
window_tile(window, WINDOW_STATE_TILE_TOP,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
window->display->screen_width,
|
||||||
|
window->display->screen_height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_top_left(struct window* window)
|
||||||
|
{
|
||||||
|
window_tile(window, WINDOW_STATE_TILE_TOP_LEFT,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
window->display->screen_width / 2,
|
||||||
|
window->display->screen_height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_top_right(struct window* window)
|
||||||
|
{
|
||||||
|
window_tile(window, WINDOW_STATE_TILE_TOP_RIGHT,
|
||||||
|
(window->display->screen_width + 1) / 2,
|
||||||
|
0,
|
||||||
|
(window->display->screen_width + 1) / 2,
|
||||||
|
window->display->screen_height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_bottom(struct window* window)
|
||||||
|
{
|
||||||
|
window_tile(window, WINDOW_STATE_TILE_BOTTOM,
|
||||||
|
0,
|
||||||
|
(window->display->screen_height + 1) / 2,
|
||||||
|
window->display->screen_width,
|
||||||
|
(window->display->screen_height + 1) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_bottom_left(struct window* window)
|
||||||
|
{
|
||||||
|
window_tile(window, WINDOW_STATE_TILE_BOTTOM_LEFT,
|
||||||
|
0,
|
||||||
|
(window->display->screen_height + 1) / 2,
|
||||||
|
window->display->screen_width / 2,
|
||||||
|
(window->display->screen_height + 1) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_tile_bottom_right(struct window* window)
|
||||||
|
{
|
||||||
|
window_tile(window, WINDOW_STATE_TILE_BOTTOM_RIGHT,
|
||||||
|
(window->display->screen_width + 1) / 2,
|
||||||
|
(window->display->screen_height + 1) / 2,
|
||||||
|
(window->display->screen_width + 1) / 2,
|
||||||
|
(window->display->screen_height + 1) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_notify_client_resize(struct window* window)
|
||||||
|
{
|
||||||
|
struct event_resize event;
|
||||||
|
event.window_id = window->window_id;
|
||||||
|
event.width = window_client_buffer(window).xres;
|
||||||
|
event.height = window_client_buffer(window).yres;
|
||||||
|
|
||||||
|
struct display_packet_header header;
|
||||||
|
header.id = EVENT_RESIZE;
|
||||||
|
header.size = sizeof(event);
|
||||||
|
|
||||||
|
assert(window->connection);
|
||||||
|
|
||||||
|
connection_schedule_transmit(window->connection, &header, sizeof(header));
|
||||||
|
connection_schedule_transmit(window->connection, &event, sizeof(event));
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2018, 2022, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* window.h
|
||||||
|
* Window abstraction.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WINDOW_H
|
||||||
|
#define WINDOW_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "framebuffer.h"
|
||||||
|
|
||||||
|
struct connection;
|
||||||
|
struct display;
|
||||||
|
|
||||||
|
static const size_t BORDER_WIDTH = 8;
|
||||||
|
static const size_t TITLE_HEIGHT = 28;
|
||||||
|
static const size_t RESIZE_GRACE = 16;
|
||||||
|
|
||||||
|
enum button_state
|
||||||
|
{
|
||||||
|
BUTTON_STATE_NORMAL,
|
||||||
|
BUTTON_STATE_HOVER,
|
||||||
|
BUTTON_STATE_PRESSED,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum window_state
|
||||||
|
{
|
||||||
|
WINDOW_STATE_REGULAR,
|
||||||
|
WINDOW_STATE_MAXIMIZED,
|
||||||
|
WINDOW_STATE_MINIMIZED,
|
||||||
|
WINDOW_STATE_TILE_LEFT,
|
||||||
|
WINDOW_STATE_TILE_RIGHT,
|
||||||
|
WINDOW_STATE_TILE_TOP,
|
||||||
|
WINDOW_STATE_TILE_TOP_LEFT,
|
||||||
|
WINDOW_STATE_TILE_TOP_RIGHT,
|
||||||
|
WINDOW_STATE_TILE_BOTTOM,
|
||||||
|
WINDOW_STATE_TILE_BOTTOM_LEFT,
|
||||||
|
WINDOW_STATE_TILE_BOTTOM_RIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct window
|
||||||
|
{
|
||||||
|
struct display* display;
|
||||||
|
struct connection* connection;
|
||||||
|
struct window* above_window;
|
||||||
|
struct window* below_window;
|
||||||
|
struct framebuffer buffer;
|
||||||
|
struct timespec title_click_time;
|
||||||
|
char* title;
|
||||||
|
ssize_t left;
|
||||||
|
ssize_t top;
|
||||||
|
size_t width;
|
||||||
|
size_t height;
|
||||||
|
ssize_t saved_left;
|
||||||
|
ssize_t saved_top;
|
||||||
|
size_t saved_width;
|
||||||
|
size_t saved_height;
|
||||||
|
uint32_t window_id;
|
||||||
|
enum window_state window_state;
|
||||||
|
enum button_state button_states[3];
|
||||||
|
bool created;
|
||||||
|
bool show;
|
||||||
|
bool focus;
|
||||||
|
bool grab_input;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct framebuffer window_client_buffer(struct window* window);
|
||||||
|
void window_schedule_redraw(struct window* window);
|
||||||
|
void window_render_frame(struct window* window);
|
||||||
|
void window_move(struct window* window, size_t left, size_t top);
|
||||||
|
void window_resize(struct window* window, size_t width, size_t height);
|
||||||
|
void window_client_resize(struct window* window, size_t client_width,
|
||||||
|
size_t client_height);
|
||||||
|
void window_initialize(struct window* window, struct connection* connection,
|
||||||
|
struct display* display, uint32_t window_id);
|
||||||
|
void window_quit(struct window* window);
|
||||||
|
void window_destroy(struct window* window);
|
||||||
|
void window_drag_resize(struct window* window, int ld, int td, int wd, int hd);
|
||||||
|
void window_on_display_resolution_change(struct window* window,
|
||||||
|
struct display* display);
|
||||||
|
void window_maximize(struct window* window);
|
||||||
|
void window_restore(struct window* window);
|
||||||
|
void window_toggle_maximized(struct window* window);
|
||||||
|
void window_tile(struct window* window, enum window_state state, size_t left,
|
||||||
|
size_t top, size_t width, size_t height);
|
||||||
|
void window_tile_leftward(struct window* window);
|
||||||
|
void window_tile_rightward(struct window* window);
|
||||||
|
void window_tile_up(struct window* window);
|
||||||
|
void window_tile_down(struct window* window);
|
||||||
|
void window_tile_left(struct window* window);
|
||||||
|
void window_tile_right(struct window* window);
|
||||||
|
void window_tile_top(struct window* window);
|
||||||
|
void window_tile_top_left(struct window* window);
|
||||||
|
void window_tile_top_right(struct window* window);
|
||||||
|
void window_tile_bottom(struct window* window);
|
||||||
|
void window_tile_bottom_left(struct window* window);
|
||||||
|
void window_tile_bottom_right(struct window* window);
|
||||||
|
void window_notify_client_resize(struct window* window);
|
||||||
|
|
||||||
|
#endif
|
|
@ -15,7 +15,7 @@ BINARIES:=\
|
||||||
asteroids \
|
asteroids \
|
||||||
aquatinspitz \
|
aquatinspitz \
|
||||||
|
|
||||||
LIBS:=-ldispd
|
LIBS:=-ldisplay
|
||||||
|
|
||||||
all: $(BINARIES)
|
all: $(BINARIES)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014, 2015, 2016, 2023 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -22,6 +22,7 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <error.h>
|
#include <error.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
@ -34,12 +35,15 @@
|
||||||
#include <timespec.h>
|
#include <timespec.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <dispd.h>
|
#include <display.h>
|
||||||
|
|
||||||
// Utility global variables every game will need.
|
// Utility global variables every game will need.
|
||||||
|
uint32_t window_id = 0;
|
||||||
|
static size_t framesize;
|
||||||
|
static uint32_t* fb;
|
||||||
static bool game_running = true;
|
static bool game_running = true;
|
||||||
static size_t game_width = 1280;
|
static size_t game_width = 800;
|
||||||
static size_t game_height = 720;
|
static size_t game_height = 512;
|
||||||
#define MAX_KEY_NUMBER 512
|
#define MAX_KEY_NUMBER 512
|
||||||
static bool keys_down[MAX_KEY_NUMBER];
|
static bool keys_down[MAX_KEY_NUMBER];
|
||||||
static bool keys_pending[MAX_KEY_NUMBER];
|
static bool keys_pending[MAX_KEY_NUMBER];
|
||||||
|
@ -200,20 +204,16 @@ void update(float deltatime)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the game into the framebuffer.
|
// Render the game into the framebuffer.
|
||||||
void render(struct dispd_window* window)
|
void render(struct display_connection* connection)
|
||||||
{
|
{
|
||||||
struct dispd_framebuffer* window_fb = dispd_begin_render(window);
|
size_t old_framesize = framesize;
|
||||||
if ( !window_fb )
|
|
||||||
{
|
|
||||||
error(0, 0, "unable to begin rendering dispd window");
|
|
||||||
game_running = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t* fb = (uint32_t*) dispd_get_framebuffer_data(window_fb);
|
size_t xres = game_width;
|
||||||
size_t xres = dispd_get_framebuffer_width(window_fb);
|
size_t yres = game_height;
|
||||||
size_t yres = dispd_get_framebuffer_height(window_fb);
|
size_t pitch = xres;
|
||||||
size_t pitch = dispd_get_framebuffer_pitch(window_fb) / sizeof(uint32_t);
|
framesize = xres * yres * sizeof(uint32_t);
|
||||||
|
if ( old_framesize != framesize && !(fb = realloc(fb, framesize)) )
|
||||||
|
err(1, "malloc");
|
||||||
|
|
||||||
// Render a colorful background.
|
// Render a colorful background.
|
||||||
for ( size_t y = 0; y < yres; y++ )
|
for ( size_t y = 0; y < yres; y++ )
|
||||||
|
@ -273,7 +273,9 @@ void render(struct dispd_window* window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispd_finish_render(window_fb);
|
display_render_window(connection, window_id, 0, 0,
|
||||||
|
game_width, game_height, fb);
|
||||||
|
display_show_window(connection, window_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... to here. No need to edit stuff below.
|
// ... to here. No need to edit stuff below.
|
||||||
|
@ -310,50 +312,68 @@ bool pop_is_key_just_down(int abskbkey)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read input from the keyboard.
|
// When the connection to the display server has disconnected.
|
||||||
void input(void)
|
void on_disconnect(void* ctx)
|
||||||
{
|
{
|
||||||
// Read the keyboard input from the user.
|
(void) ctx;
|
||||||
unsigned termmode = TERMMODE_KBKEY | TERMMODE_SIGNAL | TERMMODE_NONBLOCK;
|
exit(0);
|
||||||
if ( settermmode(0, termmode) )
|
}
|
||||||
error(1, errno, "settermmode");
|
|
||||||
uint32_t codepoint;
|
// When the window is asked to quit.
|
||||||
ssize_t numbytes;
|
void on_quit(void* ctx, uint32_t window_id)
|
||||||
while ( 0 < (numbytes = read(0, &codepoint, sizeof(codepoint))) )
|
{
|
||||||
{
|
(void) ctx;
|
||||||
int kbkey = KBKEY_DECODE(codepoint);
|
(void) window_id;
|
||||||
if( !kbkey )
|
exit(0);
|
||||||
continue;
|
}
|
||||||
int abskbkey = (kbkey < 0) ? -kbkey : kbkey;
|
|
||||||
if ( MAX_KEY_NUMBER <= (size_t) abskbkey )
|
// When the window has been resized.
|
||||||
continue;
|
void on_resize(void* ctx, uint32_t window_id, uint32_t width, uint32_t height)
|
||||||
bool is_key_down_event = 0 < kbkey;
|
{
|
||||||
if ( !keys_down[abskbkey] && is_key_down_event )
|
(void) ctx;
|
||||||
keys_pending[abskbkey] = true;
|
if ( window_id != window_id )
|
||||||
keys_down[abskbkey] = is_key_down_event;
|
return;
|
||||||
}
|
game_width = width;
|
||||||
|
game_height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When a key has been pressed.
|
||||||
|
void on_keyboard(void* ctx, uint32_t window_id, uint32_t codepoint)
|
||||||
|
{
|
||||||
|
(void) ctx;
|
||||||
|
if ( window_id != window_id )
|
||||||
|
return;
|
||||||
|
int kbkey = KBKEY_DECODE(codepoint);
|
||||||
|
if ( !kbkey )
|
||||||
|
return;
|
||||||
|
int abskbkey = (kbkey < 0) ? -kbkey : kbkey;
|
||||||
|
if ( MAX_KEY_NUMBER <= (size_t) abskbkey )
|
||||||
|
return;
|
||||||
|
bool is_key_down_event = 0 < kbkey;
|
||||||
|
if ( !keys_down[abskbkey] && is_key_down_event )
|
||||||
|
keys_pending[abskbkey] = true;
|
||||||
|
keys_down[abskbkey] = is_key_down_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the game until no longer needed.
|
// Run the game until no longer needed.
|
||||||
void mainloop(struct dispd_window* window)
|
void mainloop(struct display_connection* connection)
|
||||||
{
|
{
|
||||||
struct dispd_framebuffer* window_fb = dispd_begin_render(window);
|
struct display_event_handlers handlers = {0};
|
||||||
if ( window_fb )
|
handlers.disconnect_handler = on_disconnect;
|
||||||
{
|
handlers.quit_handler = on_quit;
|
||||||
game_width = dispd_get_framebuffer_width(window_fb);
|
handlers.resize_handler = on_resize;
|
||||||
game_height = dispd_get_framebuffer_height(window_fb);
|
handlers.keyboard_handler = on_keyboard;
|
||||||
dispd_finish_render(window_fb);
|
|
||||||
}
|
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
||||||
struct timespec last_frame_time;
|
struct timespec last_frame_time;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &last_frame_time);
|
clock_gettime(CLOCK_MONOTONIC, &last_frame_time);
|
||||||
|
|
||||||
render(window);
|
render(connection);
|
||||||
|
|
||||||
while ( game_running )
|
while ( game_running )
|
||||||
{
|
{
|
||||||
|
|
||||||
struct timespec current_frame_time;
|
struct timespec current_frame_time;
|
||||||
clock_gettime(CLOCK_MONOTONIC, ¤t_frame_time);
|
clock_gettime(CLOCK_MONOTONIC, ¤t_frame_time);
|
||||||
|
|
||||||
|
@ -361,71 +381,31 @@ void mainloop(struct dispd_window* window)
|
||||||
timespec_sub(current_frame_time, last_frame_time);
|
timespec_sub(current_frame_time, last_frame_time);
|
||||||
float deltatime = deltatime_ts.tv_sec + deltatime_ts.tv_nsec / 1E9f;
|
float deltatime = deltatime_ts.tv_sec + deltatime_ts.tv_nsec / 1E9f;
|
||||||
|
|
||||||
input();
|
while ( display_poll_event(connection, &handlers) == 0 );
|
||||||
|
|
||||||
update(deltatime);
|
update(deltatime);
|
||||||
render(window);
|
render(connection);
|
||||||
|
|
||||||
last_frame_time = current_frame_time;
|
last_frame_time = current_frame_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the terminal state when the process terminates.
|
|
||||||
static struct termios saved_tio;
|
|
||||||
|
|
||||||
static void restore_terminal_on_exit(void)
|
|
||||||
{
|
|
||||||
tcsetattr(0, TCSAFLUSH, &saved_tio);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void restore_terminal_on_signal(int signum)
|
|
||||||
{
|
|
||||||
if ( signum == SIGTSTP )
|
|
||||||
{
|
|
||||||
struct termios tio;
|
|
||||||
tcgetattr(0, &tio);
|
|
||||||
tcsetattr(0, TCSAFLUSH, &saved_tio);
|
|
||||||
raise(SIGSTOP);
|
|
||||||
tcgetattr(0, &saved_tio);
|
|
||||||
tcsetattr(0, TCSAFLUSH, &tio);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tcsetattr(0, TCSAFLUSH, &saved_tio);
|
|
||||||
raise(signum);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a display context, run the game, and then cleanly exit.
|
// Create a display context, run the game, and then cleanly exit.
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
if ( !isatty(0) )
|
struct display_connection* connection = display_connect_default();
|
||||||
error(1, errno, "standard input");
|
if ( !connection && errno == ECONNREFUSED )
|
||||||
if ( tcgetattr(0, &saved_tio) < 0 )
|
display_spawn(argc, argv);
|
||||||
error(1, errno, "tcsetattr: standard input");
|
if ( !connection )
|
||||||
if ( atexit(restore_terminal_on_exit) != 0 )
|
error(1, errno, "Could not connect to display server");
|
||||||
error(1, errno, "atexit");
|
|
||||||
struct sigaction sa;
|
|
||||||
memset(&sa, 0, sizeof(sa));
|
|
||||||
sa.sa_handler = restore_terminal_on_signal;
|
|
||||||
sigaction(SIGTSTP, &sa, NULL);
|
|
||||||
sa.sa_flags = SA_RESETHAND;
|
|
||||||
sigaction(SIGINT, &sa, NULL);
|
|
||||||
sigaction(SIGQUIT, &sa, NULL);
|
|
||||||
sigaction(SIGTERM, &sa, NULL);
|
|
||||||
|
|
||||||
if ( !dispd_initialize(&argc, &argv) )
|
display_create_window(connection, window_id);
|
||||||
error(1, 0, "couldn't initialize dispd library");
|
display_resize_window(connection, window_id, game_width, game_height);
|
||||||
struct dispd_session* session = dispd_attach_default_session();
|
display_title_window(connection, window_id, "Aquatinspitz");
|
||||||
if ( !session )
|
|
||||||
error(1, 0, "couldn't attach to dispd default session");
|
|
||||||
if ( !dispd_session_setup_game_rgba(session) )
|
|
||||||
error(1, 0, "couldn't setup dispd rgba session");
|
|
||||||
struct dispd_window* window = dispd_create_window_game_rgba(session);
|
|
||||||
if ( !window )
|
|
||||||
error(1, 0, "couldn't create dispd rgba window");
|
|
||||||
|
|
||||||
mainloop(window);
|
mainloop(connection);
|
||||||
|
|
||||||
dispd_destroy_window(window);
|
display_disconnect(connection);
|
||||||
dispd_detach_session(session);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <sys/termmode.h>
|
#include <sys/termmode.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <error.h>
|
#include <error.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -36,7 +37,11 @@
|
||||||
#include <timespec.h>
|
#include <timespec.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <dispd.h>
|
#include <display.h>
|
||||||
|
|
||||||
|
uint32_t WINDOW_ID = 0;
|
||||||
|
uint32_t WINDOW_WIDTH = 800;
|
||||||
|
uint32_t WINDOW_HEIGHT = 512;
|
||||||
|
|
||||||
static inline float RandomFloat()
|
static inline float RandomFloat()
|
||||||
{
|
{
|
||||||
|
@ -60,7 +65,7 @@ static inline float RandomAngle()
|
||||||
|
|
||||||
static inline uint32_t MakeColor(uint8_t r, uint8_t g, uint8_t b)
|
static inline uint32_t MakeColor(uint8_t r, uint8_t g, uint8_t b)
|
||||||
{
|
{
|
||||||
return b << 0UL | g << 8UL | r << 16UL;
|
return b << 0UL | g << 8UL | r << 16UL | 0xFF << 24UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const size_t STARFIELD_WIDTH = 512UL;
|
static const size_t STARFIELD_WIDTH = 512UL;
|
||||||
|
@ -110,31 +115,6 @@ bool pop_is_key_just_down(int abskbkey)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FetchKeyboardInput()
|
|
||||||
{
|
|
||||||
// Read the keyboard input from the user.
|
|
||||||
const unsigned termmode = TERMMODE_KBKEY
|
|
||||||
| TERMMODE_SIGNAL
|
|
||||||
| TERMMODE_NONBLOCK;
|
|
||||||
if ( settermmode(0, termmode) )
|
|
||||||
error(1, errno, "settermmode");
|
|
||||||
uint32_t codepoint;
|
|
||||||
ssize_t numbytes;
|
|
||||||
while ( 0 < (numbytes = read(0, &codepoint, sizeof(codepoint))) )
|
|
||||||
{
|
|
||||||
int kbkey = KBKEY_DECODE(codepoint);
|
|
||||||
if( !kbkey )
|
|
||||||
continue;
|
|
||||||
int abskbkey = (kbkey < 0) ? -kbkey : kbkey;
|
|
||||||
if ( MAXKEYNUM <= (size_t) abskbkey )
|
|
||||||
continue;
|
|
||||||
bool is_key_down_event = 0 < kbkey;
|
|
||||||
if ( !keysdown[abskbkey] && is_key_down_event )
|
|
||||||
keyspending[abskbkey] = true;
|
|
||||||
keysdown[abskbkey] = is_key_down_event;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t xres;
|
static size_t xres;
|
||||||
static size_t yres;
|
static size_t yres;
|
||||||
static size_t bpp;
|
static size_t bpp;
|
||||||
|
@ -1143,25 +1123,88 @@ void Render()
|
||||||
obj->Render();
|
obj->Render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void RunFrame(struct dispd_window* window)
|
|
||||||
|
void on_disconnect(void*)
|
||||||
{
|
{
|
||||||
struct dispd_framebuffer* fb = dispd_begin_render(window);
|
exit(0);
|
||||||
if ( !fb )
|
}
|
||||||
{
|
|
||||||
error(0, 0, "unable to begin rendering dispd window");
|
void on_quit(void*, uint32_t)
|
||||||
gamerunning = false;
|
{
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_resize(void*, uint32_t window_id, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
if ( window_id != WINDOW_ID )
|
||||||
return;
|
return;
|
||||||
|
WINDOW_WIDTH = width;
|
||||||
|
WINDOW_HEIGHT = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_keyboard(void*, uint32_t window_id, uint32_t codepoint)
|
||||||
|
{
|
||||||
|
if ( window_id != WINDOW_ID )
|
||||||
|
return;
|
||||||
|
int kbkey = KBKEY_DECODE(codepoint);
|
||||||
|
if( !kbkey )
|
||||||
|
return;
|
||||||
|
int abskbkey = (kbkey < 0) ? -kbkey : kbkey;
|
||||||
|
if ( MAXKEYNUM <= (size_t) abskbkey )
|
||||||
|
return;
|
||||||
|
bool is_key_down_event = 0 < kbkey;
|
||||||
|
if ( !keysdown[abskbkey] && is_key_down_event )
|
||||||
|
keyspending[abskbkey] = true;
|
||||||
|
keysdown[abskbkey] = is_key_down_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunFrame(struct display_connection* connection)
|
||||||
|
{
|
||||||
|
struct display_event_handlers handlers;
|
||||||
|
memset(&handlers, 0, sizeof(handlers));
|
||||||
|
handlers.disconnect_handler = on_disconnect;
|
||||||
|
handlers.quit_handler = on_quit;
|
||||||
|
handlers.resize_handler = on_resize;
|
||||||
|
handlers.keyboard_handler = on_keyboard;
|
||||||
|
|
||||||
|
while ( display_poll_event(connection, &handlers) == 0 );
|
||||||
|
|
||||||
|
size_t old_framesize = framesize;
|
||||||
|
|
||||||
|
xres = WINDOW_WIDTH;
|
||||||
|
yres = WINDOW_HEIGHT;
|
||||||
|
bpp = 32;
|
||||||
|
linesize = WINDOW_WIDTH;
|
||||||
|
framesize = WINDOW_WIDTH * sizeof(uint32_t) * WINDOW_HEIGHT;
|
||||||
|
if ( old_framesize != framesize )
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
buf = (uint32_t*) malloc(framesize);
|
||||||
|
if ( !buf )
|
||||||
|
err(1, "malloc");
|
||||||
}
|
}
|
||||||
xres = dispd_get_framebuffer_width(fb);
|
|
||||||
yres = dispd_get_framebuffer_height(fb);
|
|
||||||
bpp = dispd_get_framebuffer_format(fb);
|
|
||||||
linesize = dispd_get_framebuffer_pitch(fb) / (bpp / 8);
|
|
||||||
framesize = dispd_get_framebuffer_pitch(fb) * yres;
|
|
||||||
buf = (uint32_t*) dispd_get_framebuffer_data(fb);
|
|
||||||
FetchKeyboardInput();
|
|
||||||
GameLogic();
|
GameLogic();
|
||||||
Render();
|
Render();
|
||||||
dispd_finish_render(fb);
|
|
||||||
|
static int fps_counter = 0;
|
||||||
|
fps_counter++;
|
||||||
|
static time_t last_frame_sec = 0;
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
if ( now.tv_sec != last_frame_sec )
|
||||||
|
{
|
||||||
|
char* title = NULL;
|
||||||
|
asprintf(&title, "Asteroids (fps %i)", fps_counter);
|
||||||
|
display_title_window(connection, WINDOW_ID, title);
|
||||||
|
free(title);
|
||||||
|
fps_counter = 0;
|
||||||
|
last_frame_sec = now.tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
display_render_window(connection, WINDOW_ID, 0, 0,
|
||||||
|
WINDOW_WIDTH, WINDOW_HEIGHT, buf);
|
||||||
|
display_show_window(connection, WINDOW_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitGame()
|
void InitGame()
|
||||||
|
@ -1172,64 +1215,24 @@ void InitGame()
|
||||||
new AsteroidField;
|
new AsteroidField;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct termios saved_tio;
|
|
||||||
|
|
||||||
static void restore_terminal_on_exit(void)
|
|
||||||
{
|
|
||||||
tcsetattr(0, TCSAFLUSH, &saved_tio);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void restore_terminal_on_signal(int signum)
|
|
||||||
{
|
|
||||||
if ( signum == SIGTSTP )
|
|
||||||
{
|
|
||||||
struct termios tio;
|
|
||||||
tcgetattr(0, &tio);
|
|
||||||
tcsetattr(0, TCSAFLUSH, &saved_tio);
|
|
||||||
raise(SIGSTOP);
|
|
||||||
tcgetattr(0, &saved_tio);
|
|
||||||
tcsetattr(0, TCSAFLUSH, &tio);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tcsetattr(0, TCSAFLUSH, &saved_tio);
|
|
||||||
raise(signum);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
if ( !isatty(0) )
|
struct display_connection* connection = display_connect_default();
|
||||||
error(1, errno, "standard input");
|
if ( !connection && errno == ECONNREFUSED )
|
||||||
if ( tcgetattr(0, &saved_tio) < 0 )
|
display_spawn(argc, argv);
|
||||||
error(1, errno, "tcsetattr: standard input");
|
if ( !connection )
|
||||||
if ( atexit(restore_terminal_on_exit) != 0 )
|
error(1, errno, "Could not connect to display server");
|
||||||
error(1, errno, "atexit");
|
|
||||||
struct sigaction sa;
|
|
||||||
memset(&sa, 0, sizeof(sa));
|
|
||||||
sa.sa_handler = restore_terminal_on_signal;
|
|
||||||
sigaction(SIGTSTP, &sa, NULL);
|
|
||||||
sa.sa_flags = SA_RESETHAND;
|
|
||||||
sigaction(SIGINT, &sa, NULL);
|
|
||||||
sigaction(SIGQUIT, &sa, NULL);
|
|
||||||
sigaction(SIGTERM, &sa, NULL);
|
|
||||||
|
|
||||||
if ( !dispd_initialize(&argc, &argv) )
|
display_create_window(connection, WINDOW_ID);
|
||||||
error(1, 0, "couldn't initialize dispd library");
|
display_resize_window(connection, WINDOW_ID, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
struct dispd_session* session = dispd_attach_default_session();
|
display_title_window(connection, WINDOW_ID, "Asteroids");
|
||||||
if ( !session )
|
|
||||||
error(1, 0, "couldn't attach to dispd default session");
|
|
||||||
if ( !dispd_session_setup_game_rgba(session) )
|
|
||||||
error(1, 0, "couldn't setup dispd rgba session");
|
|
||||||
struct dispd_window* window = dispd_create_window_game_rgba(session);
|
|
||||||
if ( !window )
|
|
||||||
error(1, 0, "couldn't create dispd rgba window");
|
|
||||||
|
|
||||||
InitGame();
|
InitGame();
|
||||||
gamerunning = true;
|
gamerunning = true;
|
||||||
for ( framenum = 0; gamerunning; framenum++ )
|
for ( framenum = 0; gamerunning; framenum++ )
|
||||||
RunFrame(window);
|
RunFrame(connection);
|
||||||
|
|
||||||
dispd_destroy_window(window);
|
display_disconnect(connection);
|
||||||
dispd_detach_session(session);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
*.a
|
||||||
|
*.o
|
|
@ -0,0 +1,34 @@
|
||||||
|
SOFTWARE_MEANT_FOR_SORTIX=1
|
||||||
|
include ../build-aux/platform.mak
|
||||||
|
include ../build-aux/compiler.mak
|
||||||
|
include ../build-aux/dirs.mak
|
||||||
|
|
||||||
|
OPTLEVEL?=-g -O2
|
||||||
|
CFLAGS?=$(OPTLEVEL)
|
||||||
|
|
||||||
|
CFLAGS:=$(CFLAGS) -Wall -Wextra
|
||||||
|
CPPFLAGS:=$(CPPFLAGS) -Iinclude
|
||||||
|
|
||||||
|
LIBRARY=libdisplay.a
|
||||||
|
|
||||||
|
OBJS=\
|
||||||
|
libdisplay.o \
|
||||||
|
|
||||||
|
all: $(LIBRARY)
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p $(DESTDIR)$(LIBDIR)
|
||||||
|
cp $(LIBRARY) $(DESTDIR)$(LIBDIR)
|
||||||
|
mkdir -p $(DESTDIR)$(INCLUDEDIR)
|
||||||
|
cp -RTv include $(DESTDIR)$(INCLUDEDIR)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(LIBRARY): $(OBJS)
|
||||||
|
$(AR) rcs $@ $(OBJS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(LIBRARY) *.o *.a
|
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
* Copyright (c) 2023 Juhani 'nortti' Krekelä.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* display-protocol.h
|
||||||
|
* Display protocol.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DISPLAY_PROTOCOL_H
|
||||||
|
#define DISPLAY_PROTOCOL_H
|
||||||
|
|
||||||
|
#include <sys/display.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct display_packet_header
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_CREATE_WINDOW 0
|
||||||
|
struct display_create_window
|
||||||
|
{
|
||||||
|
uint32_t window_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_DESTROY_WINDOW 1
|
||||||
|
struct display_destroy_window
|
||||||
|
{
|
||||||
|
uint32_t window_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_RESIZE_WINDOW 2
|
||||||
|
struct display_resize_window
|
||||||
|
{
|
||||||
|
uint32_t window_id;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_RENDER_WINDOW 3
|
||||||
|
struct display_render_window
|
||||||
|
{
|
||||||
|
uint32_t window_id;
|
||||||
|
uint32_t left;
|
||||||
|
uint32_t top;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
/* width * height * sizeof(uint32_t) image bytes follows */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_TITLE_WINDOW 4
|
||||||
|
struct display_title_window
|
||||||
|
{
|
||||||
|
uint32_t window_id;
|
||||||
|
/* A non-terminated UTF-8 string follows */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_SHOW_WINDOW 5
|
||||||
|
struct display_show_window
|
||||||
|
{
|
||||||
|
uint32_t window_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_HIDE_WINDOW 6
|
||||||
|
struct display_hide_window
|
||||||
|
{
|
||||||
|
uint32_t window_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_SHUTDOWN 7
|
||||||
|
struct display_shutdown
|
||||||
|
{
|
||||||
|
uint32_t code;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_CHKBLAYOUT 8
|
||||||
|
struct display_chkblayout
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
/* keyboard layout data bytes follow */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_REQUEST_DISPLAYS 9
|
||||||
|
struct display_request_displays
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_REQUEST_DISPLAY_MODES 10
|
||||||
|
struct display_request_display_modes
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t display_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_REQUEST_DISPLAY_MODE 11
|
||||||
|
struct display_request_display_mode
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t display_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DISPLAY_SET_DISPLAY_MODE 12
|
||||||
|
struct display_set_display_mode
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t display_id;
|
||||||
|
struct dispmsg_crtc_mode mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EVENT_DISCONNECT 0
|
||||||
|
struct event_disconnect
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EVENT_QUIT 1
|
||||||
|
struct event_quit
|
||||||
|
{
|
||||||
|
uint32_t window_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EVENT_RESIZE 2
|
||||||
|
struct event_resize
|
||||||
|
{
|
||||||
|
uint32_t window_id;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EVENT_KEYBOARD 3
|
||||||
|
struct event_keyboard
|
||||||
|
{
|
||||||
|
uint32_t window_id;
|
||||||
|
uint32_t codepoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EVENT_ACK 4
|
||||||
|
struct event_ack
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
int32_t error;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EVENT_DISPLAYS 5
|
||||||
|
struct event_displays
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t displays;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EVENT_DISPLAY_MODES 6
|
||||||
|
struct event_display_modes
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t modes_count;
|
||||||
|
/* modes_count * sizeof(struct dispmsg_crtc_mode) video mode bytes follow */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EVENT_DISPLAY_MODE 7
|
||||||
|
struct event_display_mode
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
struct dispmsg_crtc_mode mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2017, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
* Copyright (c) 2023 Juhani 'nortti' Krekelä.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* display.h
|
||||||
|
* Display client library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDE_DISPLAY_H
|
||||||
|
#define INCLUDE_DISPLAY_H
|
||||||
|
|
||||||
|
#include <sys/display.h>
|
||||||
|
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct display_connection;
|
||||||
|
|
||||||
|
int display_spawn(int argc, char** argv);
|
||||||
|
|
||||||
|
struct display_connection* display_connect(const char* socket_path);
|
||||||
|
struct display_connection* display_connect_default(void);
|
||||||
|
void display_disconnect(struct display_connection* connection);
|
||||||
|
int display_connection_fd(struct display_connection* connection);
|
||||||
|
void display_shutdown(struct display_connection* connection, uint32_t code);
|
||||||
|
void display_create_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id);
|
||||||
|
void display_destroy_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id);
|
||||||
|
void display_resize_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height);
|
||||||
|
void display_render_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id,
|
||||||
|
uint32_t left,
|
||||||
|
uint32_t top,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t* data);
|
||||||
|
void display_title_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id,
|
||||||
|
const char* title);
|
||||||
|
void display_show_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id);
|
||||||
|
void display_hide_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id);
|
||||||
|
void display_chkblayout(struct display_connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
void* data,
|
||||||
|
uint32_t kblayout_bytes);
|
||||||
|
void display_request_displays(struct display_connection* connection,
|
||||||
|
uint32_t id);
|
||||||
|
void display_request_display_modes(struct display_connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
uint32_t display_id);
|
||||||
|
void display_request_display_mode(struct display_connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
uint32_t display_id);
|
||||||
|
void display_set_display_mode(struct display_connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
uint32_t display_id,
|
||||||
|
struct dispmsg_crtc_mode mode);
|
||||||
|
|
||||||
|
typedef void (*display_event_disconnect_handler_t)(void*);
|
||||||
|
typedef void (*display_event_quit_handler_t)(void*, uint32_t);
|
||||||
|
typedef void (*display_event_resize_handler_t)(void*, uint32_t, uint32_t,
|
||||||
|
uint32_t);
|
||||||
|
typedef void (*display_event_keyboard_handler_t)(void*, uint32_t, uint32_t);
|
||||||
|
typedef void (*display_event_ack_handler_t)(void*, uint32_t, int32_t);
|
||||||
|
typedef void (*display_event_displays_handler_t)(void*, uint32_t, uint32_t);
|
||||||
|
typedef void (*display_event_display_modes_handler_t)(void*, uint32_t, uint32_t,
|
||||||
|
void*, size_t);
|
||||||
|
typedef void (*display_event_display_mode_handler_t)(void*, uint32_t,
|
||||||
|
struct dispmsg_crtc_mode);
|
||||||
|
|
||||||
|
struct display_event_handlers
|
||||||
|
{
|
||||||
|
void* context;
|
||||||
|
display_event_disconnect_handler_t disconnect_handler;
|
||||||
|
display_event_quit_handler_t quit_handler;
|
||||||
|
display_event_resize_handler_t resize_handler;
|
||||||
|
display_event_keyboard_handler_t keyboard_handler;
|
||||||
|
display_event_ack_handler_t ack_handler;
|
||||||
|
display_event_displays_handler_t displays_handler;
|
||||||
|
display_event_display_modes_handler_t display_modes_handler;
|
||||||
|
display_event_display_mode_handler_t display_mode_handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
int display_poll_event(struct display_connection* connection,
|
||||||
|
struct display_event_handlers* handlers);
|
||||||
|
int display_wait_event(struct display_connection* connection,
|
||||||
|
struct display_event_handlers* handlers);
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,441 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2017, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
* Copyright (c) 2023 Juhani 'nortti' Krekelä.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* libdisplay.c
|
||||||
|
* Display client library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#include <endian.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ioleast.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
#include "display-protocol.h"
|
||||||
|
|
||||||
|
int display_spawn(int argc, char** argv)
|
||||||
|
{
|
||||||
|
int length = 2 + 1;
|
||||||
|
if ( __builtin_add_overflow(length, argc, &length) )
|
||||||
|
return errno = EOVERFLOW, -1;
|
||||||
|
char** new_argv = reallocarray(NULL, length, sizeof(char*));
|
||||||
|
if ( !new_argv )
|
||||||
|
return -1;
|
||||||
|
new_argv[0] = (char*) "display";
|
||||||
|
// TODO: Start the compositor in a special close-after-program-exists mode?
|
||||||
|
// And maybe go fullscreen / maximized by default?
|
||||||
|
new_argv[1] = (char*) "--";
|
||||||
|
for ( int i = 0; i < argc; i++ )
|
||||||
|
new_argv[2 + i] = argv[i];
|
||||||
|
new_argv[2 + argc] = NULL;
|
||||||
|
execvp(new_argv[0], new_argv);
|
||||||
|
free(new_argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int open_local_client_socket(const char* path, int flags)
|
||||||
|
{
|
||||||
|
size_t path_length = strlen(path);
|
||||||
|
size_t addr_size = offsetof(struct sockaddr_un, sun_path) + path_length + 1;
|
||||||
|
struct sockaddr_un* sockaddr = malloc(addr_size);
|
||||||
|
if ( !sockaddr )
|
||||||
|
return -1;
|
||||||
|
sockaddr->sun_family = AF_LOCAL;
|
||||||
|
strcpy(sockaddr->sun_path, path);
|
||||||
|
int fd = socket(AF_LOCAL, SOCK_STREAM | flags, 0);
|
||||||
|
if ( fd < 0 )
|
||||||
|
return free(sockaddr), -1;
|
||||||
|
if ( connect(fd, (const struct sockaddr*) sockaddr, addr_size) < 0 )
|
||||||
|
return close(fd), free(sockaddr), -1;
|
||||||
|
free(sockaddr);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct display_connection
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
struct display_packet_header header;
|
||||||
|
size_t header_got;
|
||||||
|
uint8_t* payload;
|
||||||
|
size_t payload_got;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct display_connection* display_connect(const char* socket_path)
|
||||||
|
{
|
||||||
|
struct display_connection* connection =
|
||||||
|
calloc(1, sizeof(struct display_connection));
|
||||||
|
if ( !connection )
|
||||||
|
return NULL;
|
||||||
|
if ( (connection->fd = open_local_client_socket(socket_path, 0)) < 0 )
|
||||||
|
return free(connection), (struct display_connection*) NULL;
|
||||||
|
size_t send_buffer_size = 2 * 1024 * 1024;
|
||||||
|
setsockopt(connection->fd, SOL_SOCKET, SO_SNDBUF, &send_buffer_size,
|
||||||
|
sizeof(send_buffer_size));
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct display_connection* display_connect_default(void)
|
||||||
|
{
|
||||||
|
return display_connect(getenv("DISPLAY_SOCKET") ?
|
||||||
|
getenv("DISPLAY_SOCKET") :
|
||||||
|
"/var/run/display");
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_disconnect(struct display_connection* connection)
|
||||||
|
{
|
||||||
|
free(connection->payload);
|
||||||
|
close(connection->fd);
|
||||||
|
free(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
int display_connection_fd(struct display_connection* connection)
|
||||||
|
{
|
||||||
|
return connection->fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_message(struct display_connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
const void* message,
|
||||||
|
size_t message_size,
|
||||||
|
const void* auxiliary,
|
||||||
|
size_t auxiliary_size)
|
||||||
|
{
|
||||||
|
struct display_packet_header header;
|
||||||
|
header.id = id;
|
||||||
|
header.size = message_size + auxiliary_size;
|
||||||
|
writeall(connection->fd, &header, sizeof(header));
|
||||||
|
writeall(connection->fd, message, message_size);
|
||||||
|
writeall(connection->fd, auxiliary, auxiliary_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_message_no_aux(struct display_connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
const void* message,
|
||||||
|
size_t message_size)
|
||||||
|
{
|
||||||
|
send_message(connection, id, message, message_size, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_shutdown(struct display_connection* connection, uint32_t code)
|
||||||
|
{
|
||||||
|
struct display_shutdown msg;
|
||||||
|
msg.code = code;
|
||||||
|
send_message_no_aux(connection, DISPLAY_SHUTDOWN, &msg, sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_create_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id)
|
||||||
|
{
|
||||||
|
struct display_create_window msg;
|
||||||
|
msg.window_id = window_id;
|
||||||
|
send_message_no_aux(connection, DISPLAY_CREATE_WINDOW, &msg, sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_destroy_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id)
|
||||||
|
{
|
||||||
|
struct display_destroy_window msg;
|
||||||
|
msg.window_id = window_id;
|
||||||
|
send_message_no_aux(connection, DISPLAY_DESTROY_WINDOW, &msg, sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_resize_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height)
|
||||||
|
{
|
||||||
|
struct display_resize_window msg;
|
||||||
|
msg.window_id = window_id;
|
||||||
|
msg.width = width;
|
||||||
|
msg.height = height;
|
||||||
|
send_message_no_aux(connection, DISPLAY_RESIZE_WINDOW, &msg, sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_render_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id,
|
||||||
|
uint32_t left,
|
||||||
|
uint32_t top,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t* data)
|
||||||
|
{
|
||||||
|
struct display_render_window msg;
|
||||||
|
msg.window_id = window_id;
|
||||||
|
msg.left = left;
|
||||||
|
msg.top = top;
|
||||||
|
msg.width = width;
|
||||||
|
msg.height = height;
|
||||||
|
send_message(connection, DISPLAY_RENDER_WINDOW, &msg, sizeof(msg),
|
||||||
|
data, sizeof(uint32_t) * width * height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_title_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id,
|
||||||
|
const char* title)
|
||||||
|
{
|
||||||
|
struct display_title_window msg;
|
||||||
|
msg.window_id = window_id;
|
||||||
|
send_message(connection, DISPLAY_TITLE_WINDOW, &msg, sizeof(msg), title,
|
||||||
|
strlen(title));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_show_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id)
|
||||||
|
{
|
||||||
|
struct display_show_window msg;
|
||||||
|
msg.window_id = window_id;
|
||||||
|
send_message_no_aux(connection, DISPLAY_SHOW_WINDOW, &msg, sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_hide_window(struct display_connection* connection,
|
||||||
|
uint32_t window_id)
|
||||||
|
{
|
||||||
|
struct display_hide_window msg;
|
||||||
|
msg.window_id = window_id;
|
||||||
|
send_message_no_aux(connection, DISPLAY_HIDE_WINDOW, &msg, sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_chkblayout(struct display_connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
void* data,
|
||||||
|
uint32_t kblayout_bytes)
|
||||||
|
{
|
||||||
|
struct display_chkblayout msg;
|
||||||
|
msg.id = id;
|
||||||
|
send_message(connection, DISPLAY_CHKBLAYOUT, &msg, sizeof(msg),
|
||||||
|
data, kblayout_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_request_displays(struct display_connection* connection,
|
||||||
|
uint32_t id)
|
||||||
|
{
|
||||||
|
struct display_request_displays msg;
|
||||||
|
msg.id = id;
|
||||||
|
send_message_no_aux(connection, DISPLAY_REQUEST_DISPLAYS, &msg,
|
||||||
|
sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_request_display_modes(struct display_connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
uint32_t display_id)
|
||||||
|
{
|
||||||
|
struct display_request_display_modes msg;
|
||||||
|
msg.id = id;
|
||||||
|
msg.display_id = display_id;
|
||||||
|
send_message_no_aux(connection, DISPLAY_REQUEST_DISPLAY_MODES, &msg,
|
||||||
|
sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_request_display_mode(struct display_connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
uint32_t display_id)
|
||||||
|
{
|
||||||
|
struct display_request_display_mode msg;
|
||||||
|
msg.id = id;
|
||||||
|
msg.display_id = display_id;
|
||||||
|
send_message_no_aux(connection, DISPLAY_REQUEST_DISPLAY_MODE, &msg,
|
||||||
|
sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_set_display_mode(struct display_connection* connection,
|
||||||
|
uint32_t id,
|
||||||
|
uint32_t display_id,
|
||||||
|
struct dispmsg_crtc_mode mode)
|
||||||
|
{
|
||||||
|
struct display_set_display_mode msg;
|
||||||
|
msg.id = id;
|
||||||
|
msg.display_id = display_id;
|
||||||
|
msg.mode = mode;
|
||||||
|
send_message_no_aux(connection, DISPLAY_SET_DISPLAY_MODE, &msg,
|
||||||
|
sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool display_read_event(struct display_connection* connection)
|
||||||
|
{
|
||||||
|
while ( connection->header_got < sizeof(connection->header) )
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
uint8_t* data = (uint8_t*) &connection->header + connection->header_got;
|
||||||
|
size_t left = sizeof(connection->header) - connection->header_got;
|
||||||
|
ssize_t amount = read(connection->fd, data, left);
|
||||||
|
if ( amount < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) )
|
||||||
|
break;
|
||||||
|
if ( amount <= 0 )
|
||||||
|
return false;
|
||||||
|
connection->header_got += amount;
|
||||||
|
}
|
||||||
|
if ( connection->header_got == sizeof(connection->header) &&
|
||||||
|
!connection->payload )
|
||||||
|
{
|
||||||
|
connection->payload = malloc(connection->header.size);
|
||||||
|
if ( !connection->payload )
|
||||||
|
return false;
|
||||||
|
connection->payload_got = 0;
|
||||||
|
}
|
||||||
|
while ( connection->header_got == sizeof(connection->header) &&
|
||||||
|
connection->payload &&
|
||||||
|
connection->payload_got < connection->header.size )
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
uint8_t* data = connection->payload + connection->payload_got;
|
||||||
|
size_t left = connection->header.size - connection->payload_got;
|
||||||
|
ssize_t amount = read(connection->fd, data, left);
|
||||||
|
if ( amount < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) )
|
||||||
|
break;
|
||||||
|
if ( amount <= 0 )
|
||||||
|
return false;
|
||||||
|
connection->payload_got += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int display_dispatch_event(struct display_connection* connection,
|
||||||
|
struct display_event_handlers* handlers)
|
||||||
|
{
|
||||||
|
if ( connection->header_got == sizeof(connection->header) &&
|
||||||
|
connection->payload &&
|
||||||
|
connection->payload_got == connection->header.size )
|
||||||
|
{
|
||||||
|
void* payload = connection->payload;
|
||||||
|
|
||||||
|
if ( connection->header.id == EVENT_DISCONNECT &&
|
||||||
|
connection->header.size == sizeof(struct event_disconnect) )
|
||||||
|
{
|
||||||
|
struct event_disconnect* event = payload;
|
||||||
|
(void) event;
|
||||||
|
if ( handlers->disconnect_handler )
|
||||||
|
handlers->disconnect_handler(handlers->context);
|
||||||
|
else
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( connection->header.id == EVENT_QUIT &&
|
||||||
|
connection->header.size == sizeof(struct event_quit) )
|
||||||
|
{
|
||||||
|
struct event_quit* event = payload;
|
||||||
|
if ( handlers->quit_handler )
|
||||||
|
handlers->quit_handler(handlers->context, event->window_id);
|
||||||
|
else
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( connection->header.id == EVENT_RESIZE &&
|
||||||
|
connection->header.size == sizeof(struct event_resize) )
|
||||||
|
{
|
||||||
|
struct event_resize* event = payload;
|
||||||
|
if ( handlers->resize_handler )
|
||||||
|
handlers->resize_handler(handlers->context, event->window_id,
|
||||||
|
event->width, event->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( connection->header.id == EVENT_KEYBOARD &&
|
||||||
|
connection->header.size == sizeof(struct event_keyboard) )
|
||||||
|
{
|
||||||
|
struct event_keyboard* event = payload;
|
||||||
|
if ( handlers->keyboard_handler )
|
||||||
|
handlers->keyboard_handler(handlers->context, event->window_id,
|
||||||
|
event->codepoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( connection->header.id == EVENT_ACK &&
|
||||||
|
connection->header.size == sizeof(struct event_ack) )
|
||||||
|
{
|
||||||
|
struct event_ack* event = payload;
|
||||||
|
if ( handlers->ack_handler )
|
||||||
|
handlers->ack_handler(handlers->context, event->id,
|
||||||
|
event->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( connection->header.id == EVENT_DISPLAYS &&
|
||||||
|
connection->header.size == sizeof(struct event_displays) )
|
||||||
|
{
|
||||||
|
struct event_displays* event = payload;
|
||||||
|
if ( handlers->displays_handler )
|
||||||
|
handlers->displays_handler(handlers->context, event->id,
|
||||||
|
event->displays);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( connection->header.id == EVENT_DISPLAY_MODES &&
|
||||||
|
connection->header.size >= sizeof(struct event_display_modes) )
|
||||||
|
{
|
||||||
|
size_t aux_size = connection->header.size -
|
||||||
|
sizeof(struct event_display_modes);
|
||||||
|
void* aux = (char*) payload + sizeof(struct event_display_modes);
|
||||||
|
struct event_display_modes* event = payload;
|
||||||
|
if ( handlers->display_modes_handler )
|
||||||
|
handlers->display_modes_handler(handlers->context, event->id,
|
||||||
|
event->modes_count,
|
||||||
|
aux, aux_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( connection->header.id == EVENT_DISPLAY_MODE &&
|
||||||
|
connection->header.size == sizeof(struct event_display_mode) )
|
||||||
|
{
|
||||||
|
struct event_display_mode* event = payload;
|
||||||
|
if ( handlers->display_mode_handler )
|
||||||
|
handlers->display_mode_handler(handlers->context, event->id,
|
||||||
|
event->mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection->header_got = 0;
|
||||||
|
free(connection->payload);
|
||||||
|
connection->payload = NULL;
|
||||||
|
connection->payload_got = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int display_event_read_hangup(struct display_event_handlers* handlers)
|
||||||
|
{
|
||||||
|
if ( handlers->disconnect_handler )
|
||||||
|
handlers->disconnect_handler(handlers->context);
|
||||||
|
else
|
||||||
|
exit(1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int display_poll_event(struct display_connection* connection,
|
||||||
|
struct display_event_handlers* handlers)
|
||||||
|
{
|
||||||
|
fcntl(connection->fd, F_SETFL, fcntl(connection->fd, F_GETFL) | O_NONBLOCK);
|
||||||
|
bool read_success = display_read_event(connection);
|
||||||
|
fcntl(connection->fd, F_SETFL, fcntl(connection->fd, F_GETFL) & ~O_NONBLOCK);
|
||||||
|
if ( !read_success )
|
||||||
|
return display_event_read_hangup(handlers);
|
||||||
|
return display_dispatch_event(connection, handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
int display_wait_event(struct display_connection* connection,
|
||||||
|
struct display_event_handlers* handlers)
|
||||||
|
{
|
||||||
|
if ( !display_read_event(connection) )
|
||||||
|
return display_event_read_hangup(handlers);
|
||||||
|
return display_dispatch_event(connection, handlers);
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
*.a
|
||||||
|
*.o
|
|
@ -0,0 +1,36 @@
|
||||||
|
SOFTWARE_MEANT_FOR_SORTIX=1
|
||||||
|
include ../build-aux/platform.mak
|
||||||
|
include ../build-aux/compiler.mak
|
||||||
|
include ../build-aux/dirs.mak
|
||||||
|
|
||||||
|
OPTLEVEL?=-g -O2
|
||||||
|
CFLAGS?=$(OPTLEVEL)
|
||||||
|
|
||||||
|
CFLAGS:=$(CFLAGS) -Wall -Wextra
|
||||||
|
CPPFLAGS:=$(CPPFLAGS) -Iinclude
|
||||||
|
|
||||||
|
LIBRARY=libui.a
|
||||||
|
|
||||||
|
OBJS=\
|
||||||
|
framebuffer.o \
|
||||||
|
pixel.o \
|
||||||
|
vgafont.o \
|
||||||
|
|
||||||
|
all: $(LIBRARY)
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p $(DESTDIR)$(LIBDIR)
|
||||||
|
cp $(LIBRARY) $(DESTDIR)$(LIBDIR)
|
||||||
|
mkdir -p $(DESTDIR)$(INCLUDEDIR)
|
||||||
|
cp -RTv include $(DESTDIR)$(INCLUDEDIR)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(LIBRARY): $(OBJS)
|
||||||
|
$(AR) rcs $@ $(OBJS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(LIBRARY) *.o *.a
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* framebuffer.c
|
||||||
|
* Framebuffer functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "framebuffer.h"
|
||||||
|
#include "pixel.h"
|
||||||
|
|
||||||
|
struct framebuffer framebuffer_crop(struct framebuffer fb,
|
||||||
|
size_t left,
|
||||||
|
size_t top,
|
||||||
|
size_t width,
|
||||||
|
size_t height)
|
||||||
|
{
|
||||||
|
// Crop the framebuffer horizontally.
|
||||||
|
if ( fb.xres < left )
|
||||||
|
left = fb.xres;
|
||||||
|
fb.buffer += left;
|
||||||
|
fb.xres -= left;
|
||||||
|
if ( width < fb.xres )
|
||||||
|
fb.xres = width;
|
||||||
|
|
||||||
|
// Crop the framebuffer vertically.
|
||||||
|
if ( fb.yres < top )
|
||||||
|
top = fb.yres;
|
||||||
|
fb.buffer += top * fb.pitch;
|
||||||
|
fb.yres -= top;
|
||||||
|
if ( height < fb.yres )
|
||||||
|
fb.yres = height;
|
||||||
|
|
||||||
|
return fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void framebuffer_copy_to_framebuffer(const struct framebuffer dst,
|
||||||
|
const struct framebuffer src)
|
||||||
|
{
|
||||||
|
for ( size_t y = 0; y < src.yres; y++ )
|
||||||
|
for ( size_t x = 0; x < src.xres; x++ )
|
||||||
|
framebuffer_set_pixel(dst, x, y, framebuffer_get_pixel(src, x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
void framebuffer_copy_to_framebuffer_blend(const struct framebuffer dst,
|
||||||
|
const struct framebuffer src)
|
||||||
|
{
|
||||||
|
for ( size_t y = 0; y < src.yres; y++ )
|
||||||
|
{
|
||||||
|
for ( size_t x = 0; x < src.xres; x++ )
|
||||||
|
{
|
||||||
|
uint32_t bg = framebuffer_get_pixel(dst, x, y);
|
||||||
|
uint32_t fg = framebuffer_get_pixel(src, x, y);
|
||||||
|
framebuffer_set_pixel(dst, x, y, blend_pixel(bg, fg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* framebuffer.h
|
||||||
|
* Framebuffer functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FRAMEBUFFER_H
|
||||||
|
#define FRAMEBUFFER_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct framebuffer
|
||||||
|
{
|
||||||
|
size_t pitch;
|
||||||
|
uint32_t* buffer;
|
||||||
|
size_t xres;
|
||||||
|
size_t yres;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint32_t framebuffer_get_pixel(const struct framebuffer fb,
|
||||||
|
size_t x,
|
||||||
|
size_t y)
|
||||||
|
{
|
||||||
|
if ( fb.xres <= x || fb.yres <= y )
|
||||||
|
return 0;
|
||||||
|
return fb.buffer[y * fb.pitch + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void framebuffer_set_pixel(const struct framebuffer fb,
|
||||||
|
size_t x,
|
||||||
|
size_t y,
|
||||||
|
uint32_t value)
|
||||||
|
{
|
||||||
|
if ( fb.xres <= x || fb.yres <= y )
|
||||||
|
return;
|
||||||
|
fb.buffer[y * fb.pitch + x] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct framebuffer framebuffer_crop(struct framebuffer fb,
|
||||||
|
size_t left,
|
||||||
|
size_t top,
|
||||||
|
size_t width,
|
||||||
|
size_t height);
|
||||||
|
void framebuffer_copy_to_framebuffer(const struct framebuffer dst,
|
||||||
|
const struct framebuffer src);
|
||||||
|
void framebuffer_copy_to_framebuffer_blend(const struct framebuffer dst,
|
||||||
|
const struct framebuffer src);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* pixel.h
|
||||||
|
* Pixel functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PIXEL_H
|
||||||
|
#define PIXEL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// TODO: This isn't the only pixel format in the world!
|
||||||
|
union color_rgba8
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t a;
|
||||||
|
};
|
||||||
|
uint32_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint32_t make_color_a(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||||
|
{
|
||||||
|
union color_rgba8 color;
|
||||||
|
color.r = r;
|
||||||
|
color.g = g;
|
||||||
|
color.b = b;
|
||||||
|
color.a = a;
|
||||||
|
return color.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t make_color(uint8_t r, uint8_t g, uint8_t b)
|
||||||
|
{
|
||||||
|
return make_color_a(r, g, b, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t blend_pixel(uint32_t bg_value, uint32_t fg_value);
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2016 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014, 2015, 2016, 2017 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -13,23 +13,29 @@
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*
|
*
|
||||||
* framebuffer.h
|
* vgafont.h
|
||||||
* Keeps track of framebuffers.
|
* VGA font rendering.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef INCLUDE_DISPD_FRAMEBUFFER_H
|
#ifndef VGAFONT_H
|
||||||
#define INCLUDE_DISPD_FRAMEBUFFER_H
|
#define VGAFONT_H
|
||||||
|
|
||||||
struct dispd_framebuffer
|
#include <stdint.h>
|
||||||
{
|
|
||||||
struct dispd_window* window;
|
#include "framebuffer.h"
|
||||||
uint8_t* data;
|
|
||||||
size_t datasize;
|
#define FONT_REALWIDTH 8
|
||||||
size_t pitch;
|
#define FONT_WIDTH 9
|
||||||
int bpp;
|
#define FONT_HEIGHT 16
|
||||||
int width;
|
#define FONT_CHARSIZE (FONT_REALWIDTH * FONT_HEIGHT / 8)
|
||||||
int height;
|
#define FONT_NUMCHARS 256
|
||||||
uint64_t fb_location;
|
|
||||||
};
|
extern uint8_t font[FONT_CHARSIZE * FONT_NUMCHARS];
|
||||||
|
|
||||||
|
void load_font(void);
|
||||||
|
void render_char(struct framebuffer fb, wchar_t c, uint32_t color);
|
||||||
|
void render_text(struct framebuffer fb, const char* str, uint32_t color);
|
||||||
|
size_t render_text_columns(const char* str);
|
||||||
|
size_t render_text_width(const char* str);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -13,30 +13,26 @@
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*
|
*
|
||||||
* session.h
|
* pixel.c
|
||||||
* Handles session management.
|
* Pixel functions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef INCLUDE_DISPD_SESSION_H
|
#include <stdint.h>
|
||||||
#define INCLUDE_DISPD_SESSION_H
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#include "pixel.h"
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct dispd_session
|
uint32_t blend_pixel(uint32_t bg_value, uint32_t fg_value)
|
||||||
{
|
{
|
||||||
size_t refcount;
|
union color_rgba8 fg; fg.value = fg_value;
|
||||||
uint64_t device;
|
union color_rgba8 bg; bg.value = bg_value;
|
||||||
uint64_t connector;
|
if ( fg.a == 255 )
|
||||||
struct dispd_window* current_window;
|
return fg.value;
|
||||||
bool is_rgba;
|
if ( fg.a == 0 )
|
||||||
};
|
return bg.value;
|
||||||
|
union color_rgba8 ret;
|
||||||
bool dispd__session_initialize(int* argc, char*** argv);
|
ret.a = 255;
|
||||||
|
ret.r = ((255-fg.a)*bg.r + fg.a*fg.r) / 256;
|
||||||
#if defined(__cplusplus)
|
ret.g = ((255-fg.a)*bg.g + fg.a*fg.g) / 256;
|
||||||
} /* extern "C" */
|
ret.b = ((255-fg.a)*bg.b + fg.a*fg.b) / 256;
|
||||||
#endif
|
return ret.value;
|
||||||
|
}
|
||||||
#endif
|
|
|
@ -0,0 +1,343 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2015, 2016, 2017 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* vgafont.c
|
||||||
|
* VGA font rendering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ioleast.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
#include "framebuffer.h"
|
||||||
|
#include "vgafont.h"
|
||||||
|
|
||||||
|
static const wchar_t REPLACEMENT_CHARACTER = 0xFFFD;
|
||||||
|
|
||||||
|
uint8_t font[FONT_CHARSIZE * FONT_NUMCHARS];
|
||||||
|
|
||||||
|
void load_font(void)
|
||||||
|
{
|
||||||
|
int fd = open("/dev/vgafont", O_RDONLY);
|
||||||
|
if ( fd < 0 )
|
||||||
|
err(1, "/dev/vgafont");
|
||||||
|
if ( readall(fd, font, sizeof(font)) != sizeof(font) )
|
||||||
|
err(1, "/dev/vgafont");
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Code_page_437
|
||||||
|
static inline int map_wide_to_vga_font(wchar_t c)
|
||||||
|
{
|
||||||
|
if ( 32 <= c && c < 127 )
|
||||||
|
return (int) c;
|
||||||
|
switch ( c )
|
||||||
|
{
|
||||||
|
case L'☺': return 1;
|
||||||
|
case L'☻': return 2;
|
||||||
|
case L'♥': return 3;
|
||||||
|
case L'♦': return 4;
|
||||||
|
case L'♣': return 5;
|
||||||
|
case L'♠': return 6;
|
||||||
|
case L'•': return 7;
|
||||||
|
case L'◘': return 8;
|
||||||
|
case L'○': return 9;
|
||||||
|
case L'◙': return 10;
|
||||||
|
case L'♂': return 11;
|
||||||
|
case L'♀': return 12;
|
||||||
|
case L'♪': return 13;
|
||||||
|
case L'♬': return 14;
|
||||||
|
case L'☼': return 15;
|
||||||
|
case L'►': return 16;
|
||||||
|
case L'◄': return 17;
|
||||||
|
case L'↕': return 18;
|
||||||
|
case L'‼': return 19;
|
||||||
|
case L'¶': return 20;
|
||||||
|
case L'§': return 21;
|
||||||
|
case L'▬': return 22;
|
||||||
|
case L'↨': return 23;
|
||||||
|
case L'↑': return 24;
|
||||||
|
case L'↓': return 25;
|
||||||
|
case L'→': return 26;
|
||||||
|
case L'←': return 27;
|
||||||
|
case L'∟': return 28;
|
||||||
|
case L'↔': return 29;
|
||||||
|
case L'▲': return 30;
|
||||||
|
case L'▼': return 31;
|
||||||
|
case L'⌂': return 127;
|
||||||
|
case L'Ç': return 128;
|
||||||
|
case L'ü': return 129;
|
||||||
|
case L'é': return 130;
|
||||||
|
case L'â': return 131;
|
||||||
|
case L'ä': return 132;
|
||||||
|
case L'à': return 133;
|
||||||
|
case L'å': return 134;
|
||||||
|
case L'ç': return 135;
|
||||||
|
case L'ê': return 136;
|
||||||
|
case L'ë': return 137;
|
||||||
|
case L'è': return 138;
|
||||||
|
case L'ï': return 139;
|
||||||
|
case L'î': return 140;
|
||||||
|
case L'ì': return 141;
|
||||||
|
case L'Ä': return 142;
|
||||||
|
case L'Å': return 143;
|
||||||
|
case L'É': return 144;
|
||||||
|
case L'æ': return 145;
|
||||||
|
case L'Æ': return 146;
|
||||||
|
case L'ô': return 147;
|
||||||
|
case L'ö': return 148;
|
||||||
|
case L'ò': return 149;
|
||||||
|
case L'û': return 150;
|
||||||
|
case L'ù': return 151;
|
||||||
|
case L'ÿ': return 152;
|
||||||
|
case L'Ö': return 153;
|
||||||
|
case L'Ü': return 154;
|
||||||
|
case L'¢': return 155;
|
||||||
|
case L'£': return 156;
|
||||||
|
case L'¥': return 157;
|
||||||
|
case L'₧': return 158;
|
||||||
|
case L'ƒ': return 159;
|
||||||
|
case L'á': return 160;
|
||||||
|
case L'í': return 161;
|
||||||
|
case L'ó': return 162;
|
||||||
|
case L'ú': return 163;
|
||||||
|
case L'ñ': return 164;
|
||||||
|
case L'Ñ': return 165;
|
||||||
|
case L'ª': return 166;
|
||||||
|
case L'º': return 167;
|
||||||
|
case L'¿': return 168;
|
||||||
|
case L'⌐': return 169;
|
||||||
|
case L'¬': return 170;
|
||||||
|
case L'½': return 171;
|
||||||
|
case L'¼': return 172;
|
||||||
|
case L'¡': return 173;
|
||||||
|
case L'«': return 174;
|
||||||
|
case L'»': return 175;
|
||||||
|
case L'░': return 176;
|
||||||
|
case L'▒': return 177;
|
||||||
|
case L'▓': return 178;
|
||||||
|
case L'│': return 179;
|
||||||
|
case L'┤': return 180;
|
||||||
|
case L'╡': return 181;
|
||||||
|
case L'╢': return 182;
|
||||||
|
case L'╖': return 183;
|
||||||
|
case L'╕': return 184;
|
||||||
|
case L'╣': return 185;
|
||||||
|
case L'║': return 186;
|
||||||
|
case L'╗': return 187;
|
||||||
|
case L'╝': return 188;
|
||||||
|
case L'╜': return 189;
|
||||||
|
case L'╛': return 190;
|
||||||
|
case L'┐': return 191;
|
||||||
|
case L'└': return 192;
|
||||||
|
case L'┴': return 193;
|
||||||
|
case L'┬': return 194;
|
||||||
|
case L'├': return 195;
|
||||||
|
case L'─': return 196;
|
||||||
|
case L'┼': return 197;
|
||||||
|
case L'╞': return 198;
|
||||||
|
case L'╟': return 199;
|
||||||
|
case L'╚': return 200;
|
||||||
|
case L'╔': return 201;
|
||||||
|
case L'╩': return 202;
|
||||||
|
case L'╦': return 203;
|
||||||
|
case L'╠': return 204;
|
||||||
|
case L'═': return 205;
|
||||||
|
case L'╬': return 206;
|
||||||
|
case L'╧': return 207;
|
||||||
|
case L'╨': return 208;
|
||||||
|
case L'╤': return 209;
|
||||||
|
case L'╥': return 210;
|
||||||
|
case L'╙': return 211;
|
||||||
|
case L'╘': return 212;
|
||||||
|
case L'╒': return 213;
|
||||||
|
case L'╓': return 214;
|
||||||
|
case L'╫': return 215;
|
||||||
|
case L'╪': return 216;
|
||||||
|
case L'┘': return 217;
|
||||||
|
case L'┌': return 218;
|
||||||
|
case L'█': return 219;
|
||||||
|
case L'▄': return 220;
|
||||||
|
case L'▌': return 221;
|
||||||
|
case L'▐': return 222;
|
||||||
|
case L'▀': return 223;
|
||||||
|
case L'α': return 224;
|
||||||
|
case L'ß': return 225; /* German sharp S U+00DF */
|
||||||
|
case L'β': return 225; /* Greek lowercase beta U+03B2 */
|
||||||
|
case L'Γ': return 226;
|
||||||
|
case L'π': return 227;
|
||||||
|
case L'Σ': return 228; /* Greek uppercase sigma U+03A3 */
|
||||||
|
case L'∑': return 228; /* n-ary summation sign U+2211 (replacement) */
|
||||||
|
case L'σ': return 229;
|
||||||
|
case L'µ': return 230;
|
||||||
|
case L'τ': return 231;
|
||||||
|
case L'Φ': return 232;
|
||||||
|
case L'Θ': return 233;
|
||||||
|
case L'Ω': return 234;
|
||||||
|
case L'δ': return 235; /* Greek lowercase delta U+03B4 */
|
||||||
|
case L'ð': return 235; /* Icelandic lowercase eth U+00F0 (replacement) */
|
||||||
|
case L'∂': return 235; /* Partial derivative sign U+2202 (replacement) */
|
||||||
|
case L'∞': return 236;
|
||||||
|
case L'φ': return 237; /* Greek lowercase phi U+03C6 */
|
||||||
|
case L'∅': return 237; /* Empty set sign U+2205 (replacement) */
|
||||||
|
case L'ϕ': return 237; /* Greek phi symbol in italics U+03D5 (replacement) */
|
||||||
|
case L'⌀': return 237; /* Diameter sign U+2300 (replacement) */
|
||||||
|
case L'ø': return 237; /* Latin lowercase O with stroke U+00F8 (replacement) */
|
||||||
|
case L'Ø': return 237; /* Latin uppercase O with stroke U+00D8 (replacement) */
|
||||||
|
case L'ε': return 238; /* Greek lowercase epsilon U+03B5 */
|
||||||
|
case L'∈': return 238; /* Element-of sign U+2208 */
|
||||||
|
case L'€': return 238; /* Euro sign U+20AC */
|
||||||
|
case L'∩': return 239;
|
||||||
|
case L'≡': return 240;
|
||||||
|
case L'±': return 241;
|
||||||
|
case L'≥': return 242;
|
||||||
|
case L'≤': return 243;
|
||||||
|
case L'⌠': return 244;
|
||||||
|
case L'⌡': return 245;
|
||||||
|
case L'÷': return 246;
|
||||||
|
case L'≈': return 247;
|
||||||
|
case L'°': return 248;
|
||||||
|
case L'∙': return 249;
|
||||||
|
case L'·': return 250;
|
||||||
|
case L'√': return 251;
|
||||||
|
case L'ⁿ': return 252;
|
||||||
|
case L'²': return 253;
|
||||||
|
case L'■': return 254;
|
||||||
|
default: return 0 <= c && c < 256 ? c : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t font_replacement_character[16] =
|
||||||
|
{
|
||||||
|
0b00000000,
|
||||||
|
0b00010000,
|
||||||
|
0b00111000,
|
||||||
|
0b01000100,
|
||||||
|
0b10111010,
|
||||||
|
0b10111010,
|
||||||
|
0b11110110,
|
||||||
|
0b11101110,
|
||||||
|
0b11101110,
|
||||||
|
0b11111110,
|
||||||
|
0b01101100,
|
||||||
|
0b00101000,
|
||||||
|
0b00010000,
|
||||||
|
0b00000000,
|
||||||
|
0b00000000,
|
||||||
|
0b00000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const uint8_t* get_character_font(const uint8_t* font, int remap)
|
||||||
|
{
|
||||||
|
if ( remap < 0 )
|
||||||
|
return font_replacement_character;
|
||||||
|
return font + 16 * remap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_char(struct framebuffer fb, wchar_t wc, uint32_t color)
|
||||||
|
{
|
||||||
|
// TODO: Special case the rendering of some block drawing characters like in
|
||||||
|
// the kernel so pstree looks nice.
|
||||||
|
int remap = map_wide_to_vga_font(wc);
|
||||||
|
const uint8_t* charfont = get_character_font(font, remap);
|
||||||
|
uint32_t buffer[FONT_HEIGHT * (FONT_REALWIDTH+1)];
|
||||||
|
for ( size_t y = 0; y < FONT_HEIGHT; y++ )
|
||||||
|
{
|
||||||
|
uint8_t line_bitmap = charfont[y];
|
||||||
|
for ( size_t x = 0; x < FONT_REALWIDTH; x++ )
|
||||||
|
buffer[y * (FONT_REALWIDTH+1) + x] = line_bitmap & 1U << (7 - x) ? color : 0;
|
||||||
|
uint32_t last_color = 0;
|
||||||
|
if ( 0xB0 <= remap && remap <= 0xDF && (line_bitmap & 1) )
|
||||||
|
last_color = color;
|
||||||
|
buffer[y * (FONT_REALWIDTH+1) + 8] = last_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct framebuffer character_fb;
|
||||||
|
character_fb.xres = FONT_WIDTH;
|
||||||
|
character_fb.yres = FONT_HEIGHT;
|
||||||
|
character_fb.pitch = character_fb.xres;
|
||||||
|
character_fb.buffer = buffer;
|
||||||
|
|
||||||
|
framebuffer_copy_to_framebuffer_blend(fb, character_fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_text(struct framebuffer fb, const char* str, uint32_t color)
|
||||||
|
{
|
||||||
|
mbstate_t ps;
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
size_t column = 0;
|
||||||
|
for ( size_t i = 0; true; i++ )
|
||||||
|
{
|
||||||
|
wchar_t wc;
|
||||||
|
size_t amount = mbrtowc(&wc, str + i, 1, &ps);
|
||||||
|
if ( amount == (size_t) -2 )
|
||||||
|
continue;
|
||||||
|
if ( amount == (size_t) -1 )
|
||||||
|
{
|
||||||
|
wc = REPLACEMENT_CHARACTER;
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
}
|
||||||
|
if ( amount == (size_t) 0 )
|
||||||
|
break;
|
||||||
|
int width = wcwidth(wc);
|
||||||
|
if ( 0 < width )
|
||||||
|
{
|
||||||
|
render_char(framebuffer_crop(fb, FONT_REALWIDTH * column, 0,
|
||||||
|
fb.xres, fb.yres), wc, color);
|
||||||
|
column += width; // TODO: Overflow.
|
||||||
|
}
|
||||||
|
if ( amount == (size_t) -1 && str[i] == '\0' )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t render_text_columns(const char* str)
|
||||||
|
{
|
||||||
|
mbstate_t ps;
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
size_t column = 0;
|
||||||
|
for ( size_t i = 0; true; i++ )
|
||||||
|
{
|
||||||
|
wchar_t wc;
|
||||||
|
size_t amount = mbrtowc(&wc, str + i, 1, &ps);
|
||||||
|
if ( amount == (size_t) -2 )
|
||||||
|
continue;
|
||||||
|
if ( amount == (size_t) -1 )
|
||||||
|
{
|
||||||
|
wc = REPLACEMENT_CHARACTER;
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
}
|
||||||
|
if ( amount == (size_t) 0 )
|
||||||
|
break;
|
||||||
|
int width = wcwidth(wc);
|
||||||
|
if ( 0 < width )
|
||||||
|
column += width; // TODO: Overflow.
|
||||||
|
if ( amount == (size_t) -1 && str[i] == '\0' )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t render_text_width(const char* str)
|
||||||
|
{
|
||||||
|
// TODO: Overflow.
|
||||||
|
return FONT_WIDTH * render_text_columns(str);
|
||||||
|
}
|
|
@ -198,12 +198,62 @@ void render_right_text_if_needed(struct framebuffer fb, const char* str, uint32_
|
||||||
render_right_text(fb, str, color);
|
render_right_text(fb, str, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
union c { struct { uint8_t b; uint8_t g; uint8_t r; }; uint32_t v; };
|
||||||
|
|
||||||
static void render_background(struct framebuffer fb)
|
static void render_background(struct framebuffer fb)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
uint32_t bg_color = make_color(0x89 * 2/3, 0xc7 * 2/3, 0xff * 2/3);
|
uint32_t bg_color = make_color(0x89 * 2/3, 0xc7 * 2/3, 0xff * 2/3);
|
||||||
for ( size_t y = 0; y < fb.yres; y++ )
|
for ( size_t y = 0; y < fb.yres; y++ )
|
||||||
for ( size_t x = 0; x < fb.xres; x++ )
|
for ( size_t x = 0; x < fb.xres; x++ )
|
||||||
framebuffer_set_pixel(fb, x, y, bg_color);
|
framebuffer_set_pixel(fb, x, y, bg_color);
|
||||||
|
#endif
|
||||||
|
static uint32_t s;
|
||||||
|
static uint32_t t;
|
||||||
|
static bool seeded = false;
|
||||||
|
if ( !seeded )
|
||||||
|
{
|
||||||
|
s = arc4random();
|
||||||
|
t = arc4random();
|
||||||
|
seeded = true;
|
||||||
|
}
|
||||||
|
for ( size_t y = 0; y < fb.yres; y++ )
|
||||||
|
{
|
||||||
|
for ( size_t x = 0; x < fb.xres; x++ )
|
||||||
|
{
|
||||||
|
uint32_t r = 3793 * x + 6959 * y + 1889 * t + 7901 * s;
|
||||||
|
r ^= (5717 * x * 2953 * y) ^ s ^ t;
|
||||||
|
r = (r >> 24) ^ (r >> 16) ^ (r >> 8) ^ r;
|
||||||
|
union c c;
|
||||||
|
if ( x && (r & 0x3) == 2 )
|
||||||
|
c.v = framebuffer_get_pixel(fb, x - 1, y);
|
||||||
|
else if ( y && (r & 0x3) == 1 )
|
||||||
|
c.v = framebuffer_get_pixel(fb, x, y - 1);
|
||||||
|
else if ( x && y )
|
||||||
|
c.v = framebuffer_get_pixel(fb, x - 1, y - 1);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c.v = t;
|
||||||
|
c.r = (c.r & 0xc0) | (r >> 0 & 0x3f);
|
||||||
|
c.g = (c.g & 0xc0) | (r >> 4 & 0x3f);
|
||||||
|
c.b = (c.b & 0xc0) | (r >> 8 & 0x3f);
|
||||||
|
}
|
||||||
|
if ( (r & 0xf0) == 0x10 && c.r ) c.r--;
|
||||||
|
if ( (r & 0xf0) == 0x20 && c.g ) c.g--;
|
||||||
|
if ( (r & 0xf0) == 0x30 && c.b ) c.b--;
|
||||||
|
if ( (r & 0xf0) == 0x40 && c.r != 255 ) c.r++;
|
||||||
|
if ( (r & 0xf0) == 0x50 && c.g != 255 ) c.g++;
|
||||||
|
if ( (r & 0xf0) == 0x60 && c.b != 255 ) c.b++;
|
||||||
|
union c tc = {.v = t};
|
||||||
|
if ( c.r && c.r - tc.r > (int8_t) (r >> 0) + 64 ) c.r--;
|
||||||
|
if ( c.r != 255 && tc.r - c.r > (int8_t) (r >> 4) + 240 ) c.r++;
|
||||||
|
if ( c.g && c.g - tc.g > (int8_t) (r >> 8) + 64) c.g--;
|
||||||
|
if ( c.g != 255 && tc.g - c.g > (int8_t) (r >> 12) + 240 ) c.g++;
|
||||||
|
if ( c.b && c.b - tc.b > (int8_t) (r >> 16) + 64 ) c.b--;
|
||||||
|
if ( c.b != 255 && tc.b - c.b > (int8_t) (r >> 20) + 240 ) c.b++;
|
||||||
|
framebuffer_set_pixel(fb, x, y, c.v);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_pointer(struct framebuffer fb)
|
static void render_pointer(struct framebuffer fb)
|
||||||
|
|
|
@ -1,3 +1,26 @@
|
||||||
|
diff -Paur --no-dereference -- libSDL.upstream/configure libSDL/configure
|
||||||
|
--- libSDL.upstream/configure
|
||||||
|
+++ libSDL/configure
|
||||||
|
@@ -19338,7 +19338,7 @@
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
- #include <dispd.h>
|
||||||
|
+ #include <display.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main ()
|
||||||
|
@@ -19362,8 +19362,8 @@
|
||||||
|
|
||||||
|
SOURCES="$SOURCES $srcdir/src/video/sortix/*.c"
|
||||||
|
have_video=yes
|
||||||
|
- EXTRA_LDFLAGS="$EXTRA_LDFLAGS -ldispd"
|
||||||
|
- SDL_LIBS="$SDL_LIBS -ldispd"
|
||||||
|
+ EXTRA_LDFLAGS="$EXTRA_LDFLAGS -ldisplay"
|
||||||
|
+ SDL_LIBS="$SDL_LIBS -ldisplay"
|
||||||
|
fi
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for display support" >&5
|
||||||
|
$as_echo_n "checking for display support... " >&6; }
|
||||||
diff -Paur --no-dereference -- libSDL.upstream/make.sortix libSDL/make.sortix
|
diff -Paur --no-dereference -- libSDL.upstream/make.sortix libSDL/make.sortix
|
||||||
--- libSDL.upstream/make.sortix
|
--- libSDL.upstream/make.sortix
|
||||||
+++ libSDL/make.sortix
|
+++ libSDL/make.sortix
|
||||||
|
@ -10,3 +33,358 @@ diff -Paur --no-dereference -- libSDL.upstream/make.sortix libSDL/make.sortix
|
||||||
+if [ "$1" = "install" ]; then
|
+if [ "$1" = "install" ]; then
|
||||||
+rm -fv "$DESTDIR/$EXEC_PREFIX/bin/sdl-config"
|
+rm -fv "$DESTDIR/$EXEC_PREFIX/bin/sdl-config"
|
||||||
+fi
|
+fi
|
||||||
|
diff -Paur --no-dereference -- libSDL.upstream/sdl.pc libSDL/sdl.pc
|
||||||
|
--- libSDL.upstream/sdl.pc
|
||||||
|
+++ libSDL/sdl.pc
|
||||||
|
@@ -10,6 +10,6 @@
|
||||||
|
Version: 1.2.15
|
||||||
|
Requires:
|
||||||
|
Conflicts:
|
||||||
|
-Libs: -L${libdir} -lSDL -ldispd -ldisplay
|
||||||
|
-Libs.private: -lSDL -ldispd -ldisplay -liconv -lm -ldispd -ldisplay
|
||||||
|
-Cflags: -I${includedir}/SDL
|
||||||
|
+Libs: -L${libdir} -lSDL -ldisplay -ldisplay
|
||||||
|
+Libs.private: -lSDL -ldisplay -ldisplay -liconv -lm -ldisplay -ldisplay
|
||||||
|
+Cflags: -I${includedir}/SDL -D_GNU_SOURCE=1
|
||||||
|
diff -Paur --no-dereference -- libSDL.upstream/src/video/sortix/SDL_dispd.c libSDL/src/video/sortix/SDL_dispd.c
|
||||||
|
--- libSDL.upstream/src/video/sortix/SDL_dispd.c
|
||||||
|
+++ libSDL/src/video/sortix/SDL_dispd.c
|
||||||
|
@@ -28,14 +28,11 @@
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
-#include <dispd.h>
|
||||||
|
-#if __has_include(<display.h>)
|
||||||
|
-#define DISPLAY
|
||||||
|
-#include <display.h>
|
||||||
|
-#endif
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
+#include <display.h>
|
||||||
|
+
|
||||||
|
#include "SDL_video.h"
|
||||||
|
#include "../SDL_sysvideo.h"
|
||||||
|
#include "../../events/SDL_sysevents.h"
|
||||||
|
@@ -64,30 +61,12 @@
|
||||||
|
|
||||||
|
static void DispD_DeleteDevice(SDL_VideoDevice *device)
|
||||||
|
{
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
if ( device->hidden->connection ) {
|
||||||
|
display_destroy_window(device->hidden->connection,
|
||||||
|
device->hidden->window_id);
|
||||||
|
display_disconnect(device->hidden->connection);
|
||||||
|
device->hidden->connection = NULL;
|
||||||
|
}
|
||||||
|
-#endif
|
||||||
|
- if ( device->hidden->fbinfo ) {
|
||||||
|
- dispd_finish_render(device->hidden->fbinfo);
|
||||||
|
- device->hidden->fbinfo = NULL;
|
||||||
|
- }
|
||||||
|
- if ( device->hidden->window ) {
|
||||||
|
- dispd_destroy_window(device->hidden->window);
|
||||||
|
- device->hidden->window = NULL;
|
||||||
|
- }
|
||||||
|
- if ( device->hidden->session ) {
|
||||||
|
- dispd_detach_session(device->hidden->session);
|
||||||
|
- device->hidden->session = NULL;
|
||||||
|
- }
|
||||||
|
- if ( 0 < device->hidden->tty_fd ) {
|
||||||
|
- close(device->hidden->tty_fd);
|
||||||
|
- device->hidden->tty_fd = -1;
|
||||||
|
- }
|
||||||
|
SDL_free(device->hidden);
|
||||||
|
SDL_free(device);
|
||||||
|
}
|
||||||
|
@@ -112,68 +91,20 @@
|
||||||
|
}
|
||||||
|
SDL_memset(device->hidden, 0, (sizeof *device->hidden));
|
||||||
|
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
- if ( (device->hidden->connection = display_connect_default()) ) {
|
||||||
|
- device->hidden->disconnected = 0;
|
||||||
|
- device->hidden->window_id = 0;
|
||||||
|
- device->hidden->window_width = 0;
|
||||||
|
- device->hidden->window_height = 0;
|
||||||
|
- display_create_window(device->hidden->connection,
|
||||||
|
- device->hidden->window_id);
|
||||||
|
- } else {
|
||||||
|
-#endif
|
||||||
|
- static int has_initialized_dispd = 0;
|
||||||
|
- if ( !has_initialized_dispd ) {
|
||||||
|
- if ( !dispd_initialize(NULL, NULL) ) {
|
||||||
|
- return(0);
|
||||||
|
- }
|
||||||
|
- has_initialized_dispd = 1;
|
||||||
|
- }
|
||||||
|
- if ( (device->hidden->tty_fd = open("/dev/tty", O_RDONLY)) < 0 ) {
|
||||||
|
- DispD_DeleteDevice(device);
|
||||||
|
- return(0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if ( (device->hidden->session = dispd_attach_default_session()) == NULL ) {
|
||||||
|
- DispD_DeleteDevice(device);
|
||||||
|
- return(0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if ( !(dispd_session_setup_game_rgba(device->hidden->session)) ) {
|
||||||
|
- DispD_DeleteDevice(device);
|
||||||
|
- return(0);
|
||||||
|
- }
|
||||||
|
+ if ( !(device->hidden->connection = display_connect_default()) ) {
|
||||||
|
+ return(0);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- if ( (device->hidden->window =
|
||||||
|
- dispd_create_window_game_rgba(device->hidden->session)) == NULL ) {
|
||||||
|
- DispD_DeleteDevice(device);
|
||||||
|
- return(0);
|
||||||
|
- }
|
||||||
|
+ device->hidden->disconnected = 0;
|
||||||
|
+ device->hidden->window_id = 0;
|
||||||
|
+ device->hidden->window_width = 0;
|
||||||
|
+ device->hidden->window_height = 0;
|
||||||
|
+ display_create_window(device->hidden->connection,
|
||||||
|
+ device->hidden->window_id);
|
||||||
|
|
||||||
|
- if ( (device->hidden->fbinfo =
|
||||||
|
- dispd_begin_render(device->hidden->window)) == NULL ) {
|
||||||
|
- DispD_DeleteDevice(device);
|
||||||
|
- return(0);
|
||||||
|
- }
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
+ device->hidden->current_mode.w = 800;
|
||||||
|
+ device->hidden->current_mode.h = 600;
|
||||||
|
|
||||||
|
- device->hidden->current_mode.x = 0;
|
||||||
|
- device->hidden->current_mode.y = 0;
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
- if ( device->hidden->connection ) {
|
||||||
|
- device->hidden->current_mode.w = 800;
|
||||||
|
- device->hidden->current_mode.h = 600;
|
||||||
|
- } else {
|
||||||
|
-#endif
|
||||||
|
- device->hidden->current_mode.w =
|
||||||
|
- dispd_get_framebuffer_width(device->hidden->fbinfo);
|
||||||
|
- device->hidden->current_mode.h =
|
||||||
|
- dispd_get_framebuffer_height(device->hidden->fbinfo);
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
device->hidden->mode_list[0] = &device->hidden->current_mode;
|
||||||
|
device->hidden->mode_list[1] = NULL;
|
||||||
|
|
||||||
|
@@ -217,13 +148,8 @@
|
||||||
|
SDL_Rect **DispD_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
|
||||||
|
{
|
||||||
|
// TODO: Return NULL if the format isn't 32-bit supported.
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
- if ( this->hidden->connection ) {
|
||||||
|
- // TODO: qemu seems to pick too little a resolution due to this.
|
||||||
|
- return((SDL_Rect **)-1);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
- return(this->hidden->mode_list);
|
||||||
|
+ // TODO: qemu seems to pick too little a resolution due to this.
|
||||||
|
+ return((SDL_Rect **)-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface *DispD_SetVideoMode(_THIS, SDL_Surface *current,
|
||||||
|
@@ -234,28 +160,16 @@
|
||||||
|
|
||||||
|
bpp = 32;
|
||||||
|
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
- if ( this->hidden->connection ) {
|
||||||
|
- current->flags = SDL_RESIZABLE;
|
||||||
|
- size_t size = (size_t)width * (size_t)width * (bpp / 8);
|
||||||
|
- data = SDL_malloc(size);
|
||||||
|
- if ( !data )
|
||||||
|
- return(NULL);
|
||||||
|
- this->hidden->window_width = width;
|
||||||
|
- this->hidden->window_height = height;
|
||||||
|
- display_resize_window(this->hidden->connection,
|
||||||
|
- this->hidden->window_id, width, height);
|
||||||
|
- pitch = (size_t) width * (bpp / 8);
|
||||||
|
- } else {
|
||||||
|
-#endif
|
||||||
|
- data = dispd_get_framebuffer_data(this->hidden->fbinfo);
|
||||||
|
- width = dispd_get_framebuffer_width(this->hidden->fbinfo);
|
||||||
|
- height = dispd_get_framebuffer_height(this->hidden->fbinfo);
|
||||||
|
- pitch = dispd_get_framebuffer_pitch(this->hidden->fbinfo);
|
||||||
|
- current->flags = SDL_FULLSCREEN;
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
+ current->flags = SDL_RESIZABLE;
|
||||||
|
+ size_t size = (size_t)width * (size_t)width * (bpp / 8);
|
||||||
|
+ data = SDL_malloc(size);
|
||||||
|
+ if ( !data )
|
||||||
|
+ return(NULL);
|
||||||
|
+ this->hidden->window_width = width;
|
||||||
|
+ this->hidden->window_height = height;
|
||||||
|
+ display_resize_window(this->hidden->connection,
|
||||||
|
+ this->hidden->window_id, width, height);
|
||||||
|
+ pitch = (size_t) width * (bpp / 8);
|
||||||
|
|
||||||
|
int y;
|
||||||
|
for ( y = 0; y < height; y++ )
|
||||||
|
@@ -270,7 +184,7 @@
|
||||||
|
assert(current->format);
|
||||||
|
assert(current->format->BitsPerPixel == 32);
|
||||||
|
current->pitch = pitch;
|
||||||
|
- // TODO: Memory leak of old buffer?
|
||||||
|
+ free(current->pixels);
|
||||||
|
current->pixels = data;
|
||||||
|
current->w = width;
|
||||||
|
current->h = height;
|
||||||
|
@@ -291,12 +205,8 @@
|
||||||
|
|
||||||
|
static void DispD_SetCaption(_THIS, const char *title, const char *icon)
|
||||||
|
{
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
- if ( this->hidden->connection) {
|
||||||
|
- display_title_window(this->hidden->connection,
|
||||||
|
- this->hidden->window_id, title);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
+ display_title_window(this->hidden->connection,
|
||||||
|
+ this->hidden->window_id, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need to wait for vertical retrace on page flipped displays */
|
||||||
|
@@ -312,26 +222,13 @@
|
||||||
|
|
||||||
|
static void DispD_UpdateRects(_THIS, int numrects, SDL_Rect *rects)
|
||||||
|
{
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
- if ( this->hidden->connection) {
|
||||||
|
- for ( size_t i = 3; i < (size_t)SDL_VideoSurface->w * (size_t)SDL_VideoSurface->h * 4; i += 4 )
|
||||||
|
- ((unsigned char*)SDL_VideoSurface->pixels)[i] = 255;
|
||||||
|
- display_render_window(this->hidden->connection, this->hidden->window_id,
|
||||||
|
- 0, 0, SDL_VideoSurface->w, SDL_VideoSurface->h,
|
||||||
|
- SDL_VideoSurface->pixels);
|
||||||
|
- display_show_window(this->hidden->connection, this->hidden->window_id);
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
- uint8_t* old_data = dispd_get_framebuffer_data(this->hidden->fbinfo);
|
||||||
|
- if ( !dispd_finish_render(this->hidden->fbinfo) ) {
|
||||||
|
- abort();
|
||||||
|
- }
|
||||||
|
- if ( !(this->hidden->fbinfo = dispd_begin_render(this->hidden->window)) ) {
|
||||||
|
- abort();
|
||||||
|
- }
|
||||||
|
- uint8_t* new_data = dispd_get_framebuffer_data(this->hidden->fbinfo);
|
||||||
|
- assert(old_data == new_data);
|
||||||
|
+ size_t size = (size_t)SDL_VideoSurface->w * (size_t)SDL_VideoSurface->h * 4;
|
||||||
|
+ for ( size_t i = 3; i < size; i += 4 )
|
||||||
|
+ ((unsigned char*)SDL_VideoSurface->pixels)[i] = 255;
|
||||||
|
+ display_render_window(this->hidden->connection, this->hidden->window_id,
|
||||||
|
+ 0, 0, SDL_VideoSurface->w, SDL_VideoSurface->h,
|
||||||
|
+ SDL_VideoSurface->pixels);
|
||||||
|
+ display_show_window(this->hidden->connection, this->hidden->window_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DispD_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
|
||||||
|
@@ -345,6 +242,7 @@
|
||||||
|
*/
|
||||||
|
void DispD_VideoQuit(_THIS)
|
||||||
|
{
|
||||||
|
+ free(this->screen->pixels);
|
||||||
|
this->screen->pixels = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -424,7 +322,6 @@
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
static void on_disconnect(void* ctx)
|
||||||
|
{
|
||||||
|
struct SDL_PrivateVideoData* hidden = (struct SDL_PrivateVideoData*) ctx;
|
||||||
|
@@ -463,48 +360,19 @@
|
||||||
|
keysym.unicode = 0;
|
||||||
|
SDL_PrivateKeyboard(kbkey < 0 ? SDL_RELEASED : SDL_PRESSED, &keysym);
|
||||||
|
}
|
||||||
|
-#endif
|
||||||
|
|
||||||
|
void DispD_PumpEvents(_THIS)
|
||||||
|
{
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
- if ( this->hidden->connection) {
|
||||||
|
- struct display_event_handlers handlers;
|
||||||
|
- memset(&handlers, 0, sizeof(handlers));
|
||||||
|
- handlers.context = this->hidden;
|
||||||
|
- handlers.disconnect_handler = on_disconnect;
|
||||||
|
- handlers.quit_handler = on_quit;
|
||||||
|
- handlers.resize_handler = on_resize;
|
||||||
|
- handlers.keyboard_handler = on_keyboard;
|
||||||
|
- while ( !this->hidden->disconnected ) {
|
||||||
|
- if ( display_poll_event(this->hidden->connection, &handlers) < 0 )
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
- // Read the keyboard input from the user.
|
||||||
|
- const unsigned termmode = TERMMODE_KBKEY
|
||||||
|
- | TERMMODE_UNICODE
|
||||||
|
- | TERMMODE_SIGNAL
|
||||||
|
- | TERMMODE_NONBLOCK;
|
||||||
|
- if ( settermmode(0, termmode) ) {
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
- uint32_t codepoint;
|
||||||
|
- ssize_t numbytes;
|
||||||
|
- while ( 0 < (numbytes = read(0, &codepoint, sizeof(codepoint))) )
|
||||||
|
- {
|
||||||
|
- int kbkey = KBKEY_DECODE(codepoint);
|
||||||
|
- int abskbkey = kbkey < 0 ? -kbkey : kbkey;
|
||||||
|
- int key = TranslateKey(abskbkey);
|
||||||
|
- SDL_keysym keysym;
|
||||||
|
- keysym.scancode = abskbkey;
|
||||||
|
- keysym.sym = key;
|
||||||
|
- keysym.mod = 0;
|
||||||
|
- keysym.unicode = 0;
|
||||||
|
- SDL_PrivateKeyboard(kbkey < 0 ? SDL_RELEASED : SDL_PRESSED, &keysym);
|
||||||
|
+ struct display_event_handlers handlers;
|
||||||
|
+ memset(&handlers, 0, sizeof(handlers));
|
||||||
|
+ handlers.context = this->hidden;
|
||||||
|
+ handlers.disconnect_handler = on_disconnect;
|
||||||
|
+ handlers.quit_handler = on_quit;
|
||||||
|
+ handlers.resize_handler = on_resize;
|
||||||
|
+ handlers.keyboard_handler = on_keyboard;
|
||||||
|
+ while ( !this->hidden->disconnected ) {
|
||||||
|
+ if ( display_poll_event(this->hidden->connection, &handlers) < 0 )
|
||||||
|
+ break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff -Paur --no-dereference -- libSDL.upstream/src/video/sortix/SDL_dispd.h libSDL/src/video/sortix/SDL_dispd.h
|
||||||
|
--- libSDL.upstream/src/video/sortix/SDL_dispd.h
|
||||||
|
+++ libSDL/src/video/sortix/SDL_dispd.h
|
||||||
|
@@ -33,19 +33,13 @@
|
||||||
|
/* Private display data */
|
||||||
|
|
||||||
|
struct SDL_PrivateVideoData {
|
||||||
|
-#ifdef DISPLAY
|
||||||
|
struct display_connection *connection;
|
||||||
|
uint32_t window_id;
|
||||||
|
uint32_t window_width;
|
||||||
|
uint32_t window_height;
|
||||||
|
int disconnected;
|
||||||
|
-#endif
|
||||||
|
- struct dispd_session *session;
|
||||||
|
- struct dispd_window *window;
|
||||||
|
- struct dispd_framebuffer *fbinfo;
|
||||||
|
SDL_Rect current_mode;
|
||||||
|
SDL_Rect *mode_list[2];
|
||||||
|
- int tty_fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _SDL_nullvideo_h */
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
require base no-await
|
||||||
|
require local no-await
|
||||||
|
|
||||||
|
tty tty1
|
||||||
|
need tty
|
||||||
|
|
||||||
|
cd "$HOME"
|
||||||
|
exit-code-meaning poweroff-reboot
|
||||||
|
exec display
|
|
@ -0,0 +1,8 @@
|
||||||
|
require base no-await
|
||||||
|
require local no-await
|
||||||
|
|
||||||
|
tty tty1
|
||||||
|
need tty
|
||||||
|
|
||||||
|
exec display terminal sysinstall
|
||||||
|
exit-code-meaning poweroff-reboot
|
|
@ -0,0 +1,8 @@
|
||||||
|
require base no-await
|
||||||
|
require local no-await
|
||||||
|
|
||||||
|
tty tty1
|
||||||
|
need tty
|
||||||
|
|
||||||
|
exec display terminal sysupgrade
|
||||||
|
exit-code-meaning poweroff-reboot
|
|
@ -241,6 +241,12 @@ Copy the
|
||||||
file (if it exists) into the installation?
|
file (if it exists) into the installation?
|
||||||
.It Sy empty_password Ns "=" Ns Oo Sy no "|" Sy yes Oc ( default Sy no )
|
.It Sy empty_password Ns "=" Ns Oo Sy no "|" Sy yes Oc ( default Sy no )
|
||||||
Allow insecure empty passwords for regular users?
|
Allow insecure empty passwords for regular users?
|
||||||
|
.It Sy enable_gui Ns "=" Ns Oo Sy no "|" Sy yes Oc ( default Sy yes )
|
||||||
|
Enable the
|
||||||
|
.Xr display 1
|
||||||
|
graphical user interface?
|
||||||
|
The choice is remembered in
|
||||||
|
.Xr session 5 .
|
||||||
.It Sy enable_ntpd Ns "=" Ns Oo Sy no "|" Sy yes Oc ( default Sy no )
|
.It Sy enable_ntpd Ns "=" Ns Oo Sy no "|" Sy yes Oc ( default Sy no )
|
||||||
Automatically get time from the network using
|
Automatically get time from the network using
|
||||||
.Xr ntpd 8 ?
|
.Xr ntpd 8 ?
|
||||||
|
|
|
@ -116,6 +116,16 @@ It depends on the
|
||||||
and
|
and
|
||||||
.Sy local
|
.Sy local
|
||||||
daemons.
|
daemons.
|
||||||
|
.It Sy single-user-gui
|
||||||
|
Like
|
||||||
|
.Sy single-user ,
|
||||||
|
but runs the root shell in
|
||||||
|
.Xr terminal 1
|
||||||
|
inside the
|
||||||
|
.Xr display 1
|
||||||
|
graphical user interface environment.
|
||||||
|
This operating system mode is insecure because it boots straight to root access
|
||||||
|
without a password.
|
||||||
.It Sy sysinstall
|
.It Sy sysinstall
|
||||||
Starts the operating system installer.
|
Starts the operating system installer.
|
||||||
This foreground daemon starts the
|
This foreground daemon starts the
|
||||||
|
@ -129,6 +139,16 @@ It depends on the
|
||||||
and
|
and
|
||||||
.Sy local
|
.Sy local
|
||||||
daemons.
|
daemons.
|
||||||
|
.It Sy sysinstall-gui
|
||||||
|
Like
|
||||||
|
.Sy sysinstall ,
|
||||||
|
but runs it in
|
||||||
|
.Xr terminal 1
|
||||||
|
inside the
|
||||||
|
.Xr display 1
|
||||||
|
graphical user interface environment.
|
||||||
|
This operating system mode is insecure because it boots straight to root access
|
||||||
|
without a password.
|
||||||
.It Sy sysupgrade
|
.It Sy sysupgrade
|
||||||
Starts the operating system upgrader.
|
Starts the operating system upgrader.
|
||||||
This foreground daemon starts the
|
This foreground daemon starts the
|
||||||
|
@ -142,6 +162,16 @@ It depends on the
|
||||||
and
|
and
|
||||||
.Sy local
|
.Sy local
|
||||||
daemons.
|
daemons.
|
||||||
|
.It Sy sysupgrade-gui
|
||||||
|
Like
|
||||||
|
.Sy sysupgrade ,
|
||||||
|
but runs it in
|
||||||
|
.Xr terminal 1
|
||||||
|
inside the
|
||||||
|
.Xr display 8
|
||||||
|
graphical user interface environment.
|
||||||
|
This operating system mode is insecure because it boots straight to root access
|
||||||
|
without a password.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
The following daemons are provided by the system:
|
The following daemons are provided by the system:
|
||||||
|
|
|
@ -37,6 +37,20 @@ file can be created in any text editor and then made executable:
|
||||||
editor ~/.session
|
editor ~/.session
|
||||||
chmod +x ~/.session
|
chmod +x ~/.session
|
||||||
.Ed
|
.Ed
|
||||||
|
.Ss Graphical User Interface
|
||||||
|
.Xr display 1
|
||||||
|
can be selected as the user's graphical user interface with this executable
|
||||||
|
.Pa ~/.session
|
||||||
|
script:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
#!/bin/sh
|
||||||
|
exec display
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
.Xr display 1
|
||||||
|
will run the
|
||||||
|
.Xr displayrc 5
|
||||||
|
script on startup, which can be used to start applications.
|
||||||
.Ss Trianglix
|
.Ss Trianglix
|
||||||
.Xr trianglix 1
|
.Xr trianglix 1
|
||||||
can be selected as the user's triangle environment with this executable
|
can be selected as the user's triangle environment with this executable
|
||||||
|
|
|
@ -156,6 +156,15 @@ Ports can additionally be loaded as binary packages in the
|
||||||
directory by navigating to the advanced menu and then the select binary packages
|
directory by navigating to the advanced menu and then the select binary packages
|
||||||
submenu and then selecting which ports.
|
submenu and then selecting which ports.
|
||||||
.Pp
|
.Pp
|
||||||
|
The
|
||||||
|
.Xr display 1
|
||||||
|
graphical user interface and desktop environment can be disabled by navigating
|
||||||
|
to the advanced menu and selecting
|
||||||
|
.Sy Disable GUI ,
|
||||||
|
which will instead boot to a plain
|
||||||
|
.Pa /dev/tty1
|
||||||
|
terminal.
|
||||||
|
.Pp
|
||||||
The network drivers can be disabled by navigating to the advanced menu and
|
The network drivers can be disabled by navigating to the advanced menu and
|
||||||
selecting
|
selecting
|
||||||
.Sy Disable network drivers .
|
.Sy Disable network drivers .
|
||||||
|
@ -180,6 +189,17 @@ If not, you can run the installer by running the
|
||||||
.Xr sysinstall 8
|
.Xr sysinstall 8
|
||||||
command.
|
command.
|
||||||
.Pp
|
.Pp
|
||||||
|
You will boot into the
|
||||||
|
.Xr display 1
|
||||||
|
graphical user interface and desktop environment by default.
|
||||||
|
A single
|
||||||
|
.Xr terminal 1
|
||||||
|
window will open by default.
|
||||||
|
More terminals can be opened by pressing Control + Alt + T.
|
||||||
|
See
|
||||||
|
.Xr display 1
|
||||||
|
for the available shortcuts.
|
||||||
|
.Pp
|
||||||
The installer is an interactive command line program that asks you questions and
|
The installer is an interactive command line program that asks you questions and
|
||||||
you answer them.
|
you answer them.
|
||||||
It provides useful information you shouldn't accidentally overlook.
|
It provides useful information you shouldn't accidentally overlook.
|
||||||
|
@ -386,6 +406,14 @@ and
|
||||||
.Pp
|
.Pp
|
||||||
Please note that Sortix is not currently secure as a multi-user system and
|
Please note that Sortix is not currently secure as a multi-user system and
|
||||||
filesystem permissions are not enforced.
|
filesystem permissions are not enforced.
|
||||||
|
.Ss Graphical User Interface
|
||||||
|
You will be asked if you want to enable the graphical user interface.
|
||||||
|
If you answer yes, then the system-wide default
|
||||||
|
.Xr session 5
|
||||||
|
is configured to run
|
||||||
|
.Xr display 1
|
||||||
|
upon login.
|
||||||
|
Otherwise the user's preferred shell will be run upon login.
|
||||||
.Ss Network Time
|
.Ss Network Time
|
||||||
You will be asked if you want to enable the Network Time Protocol client
|
You will be asked if you want to enable the Network Time Protocol client
|
||||||
.Xr ntpd 8 ,
|
.Xr ntpd 8 ,
|
||||||
|
@ -519,6 +547,7 @@ fragment instead.
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr chkblayout 1 ,
|
.Xr chkblayout 1 ,
|
||||||
.Xr chvideomode 1 ,
|
.Xr chvideomode 1 ,
|
||||||
|
.Xr display 1 ,
|
||||||
.Xr man 1 ,
|
.Xr man 1 ,
|
||||||
.Xr fstab 5 ,
|
.Xr fstab 5 ,
|
||||||
.Xr group 5 ,
|
.Xr group 5 ,
|
||||||
|
|
|
@ -514,6 +514,12 @@ ssh-keygen -t rsa -f liveconfig/root/.ssh/id_rsa -N "" -C "root@$hostname"
|
||||||
Consider omitting the
|
Consider omitting the
|
||||||
.Fl N
|
.Fl N
|
||||||
option and password protect the private key to protect it in the case of a leak.
|
option and password protect the private key to protect it in the case of a leak.
|
||||||
|
.Ss Boot to Console Instead of GUI By Default
|
||||||
|
To customize a release so it boots to a console instead of the GUI:
|
||||||
|
.Bd -literal
|
||||||
|
tix-iso-bootconfig --disable-gui bootconfig
|
||||||
|
tix-iso-add sortix.iso bootconfig
|
||||||
|
.Ed
|
||||||
.Ss Automatic Installation
|
.Ss Automatic Installation
|
||||||
To customize a release so it automatically installs itself per the
|
To customize a release so it automatically installs itself per the
|
||||||
.Xr autoinstall.conf 5 :
|
.Xr autoinstall.conf 5 :
|
||||||
|
|
|
@ -14,26 +14,34 @@ The installation process is covered in
|
||||||
Bootable cdrom releases will offer the options of running a live environment,
|
Bootable cdrom releases will offer the options of running a live environment,
|
||||||
installing the operating system, or upgrading an existing installation.
|
installing the operating system, or upgrading an existing installation.
|
||||||
.Pp
|
.Pp
|
||||||
You will be presented a with standard Unix command line environment upon login or
|
You will be presented with a graphical Unix-like command line environment upon
|
||||||
booting the live environment.
|
login or booting the live environment.
|
||||||
.Ss Shutdown
|
.Ss Desktop Environment
|
||||||
.Xr init 8
|
The
|
||||||
spawns a session after boot.
|
.Xr display 1
|
||||||
This is
|
desktop environment is automatically started when booting the live environment
|
||||||
.Xr login 8
|
or after logging into an installation.
|
||||||
if the system is booted in multi-user mode.
|
|
||||||
This is a root shell if booted in single-user mode.
|
|
||||||
.Pp
|
.Pp
|
||||||
To power off the computer login as user
|
A new
|
||||||
.Sy poweroff
|
.Xr terminal 1
|
||||||
or run
|
can be launched by pressing Control + Alt + T.
|
||||||
|
.Pp
|
||||||
|
The desktop environment can be exited by pressing Control + Alt + Delete,
|
||||||
|
which will return to the login screen (in installations) or power off the
|
||||||
|
computer (in the live environment).
|
||||||
|
.Pp
|
||||||
|
See
|
||||||
|
.Xr display 1
|
||||||
|
for all the available keyboard shortcuts.
|
||||||
|
.Ss Shutdown
|
||||||
|
To power off the computer, run
|
||||||
.Xr poweroff 8
|
.Xr poweroff 8
|
||||||
after logging in.
|
or login as
|
||||||
To reboot the computer login as user
|
.Sy poweroff .
|
||||||
.Sy reboot
|
To reboot the computter, run
|
||||||
or run
|
|
||||||
.Xr reboot 8
|
.Xr reboot 8
|
||||||
after logging in.
|
or login as
|
||||||
|
.Sy reboot .
|
||||||
.Ss Keyboard Layout
|
.Ss Keyboard Layout
|
||||||
The kernel has a default US keyboard layout compiled into it.
|
The kernel has a default US keyboard layout compiled into it.
|
||||||
.Pp
|
.Pp
|
||||||
|
|
|
@ -60,13 +60,13 @@ install: all
|
||||||
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-group
|
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-group
|
||||||
|
|
||||||
sysinstall: $(SYSINSTALL_OBJS)
|
sysinstall: $(SYSINSTALL_OBJS)
|
||||||
$(CC) $(SYSINSTALL_OBJS) -o $@ -lmount
|
$(CC) $(SYSINSTALL_OBJS) -o $@ -lmount -ldisplay
|
||||||
|
|
||||||
sysmerge: $(SYSMERGE_OBJS)
|
sysmerge: $(SYSMERGE_OBJS)
|
||||||
$(CC) $(SYSMERGE_OBJS) -o $@ -lmount
|
$(CC) $(SYSMERGE_OBJS) -o $@ -lmount
|
||||||
|
|
||||||
sysupgrade: $(SYSUPGRADE_OBJS)
|
sysupgrade: $(SYSUPGRADE_OBJS)
|
||||||
$(CC) $(SYSUPGRADE_OBJS) -o $@ -lmount
|
$(CC) $(SYSUPGRADE_OBJS) -o $@ -lmount -ldisplay
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) -std=gnu11 -c $< -o $@
|
$(CC) $(CFLAGS) $(CPPFLAGS) -std=gnu11 -c $< -o $@
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2016, 2017 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2015, 2016, 2017, 2023 Jonas 'Sortie' Termansen.
|
||||||
|
* Copyright (c) 2023 Juhani 'nortti' Krekelä.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -17,11 +18,15 @@
|
||||||
* Interactive utility functions.
|
* Interactive utility functions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <sys/display.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
#include <sys/termmode.h>
|
#include <sys/termmode.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <display.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
@ -31,10 +36,22 @@
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
#include "autoconf.h"
|
#include "autoconf.h"
|
||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "interactive.h"
|
#include "interactive.h"
|
||||||
|
|
||||||
|
#define REQUEST_DISPLAYS_ID 0
|
||||||
|
#define REQUEST_DISPLAY_MODE_ID 1
|
||||||
|
|
||||||
|
static uint32_t displays_count;
|
||||||
|
static bool displays_count_received;
|
||||||
|
|
||||||
|
static struct dispmsg_crtc_mode display_mode;
|
||||||
|
static int request_display_mode_error;
|
||||||
|
static bool display_mode_received;
|
||||||
|
|
||||||
void shlvl(void)
|
void shlvl(void)
|
||||||
{
|
{
|
||||||
long shlvl = 0;
|
long shlvl = 0;
|
||||||
|
@ -259,3 +276,107 @@ bool missing_program(const char* program)
|
||||||
warnx("%s: Program is absent", program);
|
warnx("%s: Program is absent", program);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void on_displays(void* ctx, uint32_t id, uint32_t displays)
|
||||||
|
{
|
||||||
|
(void) ctx;
|
||||||
|
if ( id != REQUEST_DISPLAYS_ID )
|
||||||
|
return;
|
||||||
|
displays_count = displays;
|
||||||
|
displays_count_received = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_display_mode(void* ctx, uint32_t id,
|
||||||
|
struct dispmsg_crtc_mode mode)
|
||||||
|
{
|
||||||
|
(void) ctx;
|
||||||
|
if ( id != REQUEST_DISPLAY_MODE_ID )
|
||||||
|
return;
|
||||||
|
display_mode = mode;
|
||||||
|
request_display_mode_error = 0;
|
||||||
|
display_mode_received = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_ack(void* ctx, uint32_t id, int32_t error)
|
||||||
|
{
|
||||||
|
(void) ctx;
|
||||||
|
if ( id != REQUEST_DISPLAY_MODE_ID )
|
||||||
|
return;
|
||||||
|
if ( error )
|
||||||
|
{
|
||||||
|
request_display_mode_error = error;
|
||||||
|
display_mode_received = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_video_mode(struct dispmsg_crtc_mode* mode)
|
||||||
|
{
|
||||||
|
if ( getenv("DISPLAY_SOCKET") )
|
||||||
|
{
|
||||||
|
struct display_connection* connection = display_connect_default();
|
||||||
|
if ( !connection )
|
||||||
|
return false;
|
||||||
|
struct display_event_handlers handlers = {0};
|
||||||
|
handlers.displays_handler = on_displays;
|
||||||
|
handlers.display_mode_handler = on_display_mode;
|
||||||
|
handlers.ack_handler = on_ack;
|
||||||
|
display_request_displays(connection, REQUEST_DISPLAYS_ID);
|
||||||
|
displays_count_received = false;
|
||||||
|
while ( !displays_count_received )
|
||||||
|
display_wait_event(connection, &handlers);
|
||||||
|
if ( displays_count < 1 )
|
||||||
|
{
|
||||||
|
display_disconnect(connection);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// TODO: Multimonitor support.
|
||||||
|
display_request_display_mode(connection, REQUEST_DISPLAY_MODE_ID, 0);
|
||||||
|
display_mode_received = false;
|
||||||
|
while ( !display_mode_received )
|
||||||
|
display_wait_event(connection, &handlers);
|
||||||
|
display_disconnect(connection);
|
||||||
|
if ( request_display_mode_error )
|
||||||
|
return false;
|
||||||
|
*mode = display_mode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tiocgdisplay display;
|
||||||
|
struct tiocgdisplays gdisplays;
|
||||||
|
memset(&gdisplays, 0, sizeof(gdisplays));
|
||||||
|
gdisplays.count = 1;
|
||||||
|
gdisplays.displays = &display;
|
||||||
|
struct dispmsg_get_driver_name dgdn = { 0 };
|
||||||
|
if ( ioctl(1, TIOCGDISPLAYS, &gdisplays) < 0 || gdisplays.count == 0 )
|
||||||
|
return false;
|
||||||
|
dgdn.device = display.device;
|
||||||
|
dgdn.msgid = DISPMSG_GET_DRIVER_NAME;
|
||||||
|
dgdn.device = display.device;
|
||||||
|
dgdn.driver_index = 0;
|
||||||
|
dgdn.name.byte_size = 0;
|
||||||
|
dgdn.name.str = NULL;
|
||||||
|
if ( dispmsg_issue(&dgdn, sizeof(dgdn)) < 0 && errno == ENODEV )
|
||||||
|
return false;
|
||||||
|
struct dispmsg_get_crtc_mode get_mode;
|
||||||
|
memset(&get_mode, 0, sizeof(get_mode));
|
||||||
|
get_mode.msgid = DISPMSG_GET_CRTC_MODE;
|
||||||
|
get_mode.device = display.device;
|
||||||
|
get_mode.connector = display.connector;
|
||||||
|
// TODO: Still allow setting the video mode if none was already set.
|
||||||
|
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) < 0 )
|
||||||
|
return false;
|
||||||
|
*mode = get_mode.mode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui_shutdown(int code)
|
||||||
|
{
|
||||||
|
if ( getenv("DISPLAY_SOCKET") )
|
||||||
|
{
|
||||||
|
struct display_connection* connection = display_connect_default();
|
||||||
|
if ( connection )
|
||||||
|
display_shutdown(connection, code);
|
||||||
|
else
|
||||||
|
warn("display_connect_default");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2015, 2016, 2023 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -42,5 +42,7 @@ void password(char* buffer,
|
||||||
size_t buffer_size,
|
size_t buffer_size,
|
||||||
const char* question);
|
const char* question);
|
||||||
bool missing_program(const char* program);
|
bool missing_program(const char* program);
|
||||||
|
bool get_video_mode(struct dispmsg_crtc_mode* mode);
|
||||||
|
void gui_shutdown(int code);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -348,6 +348,7 @@ static bool etc_made = false;
|
||||||
static char etc[] = "/tmp/etc.XXXXXX";
|
static char etc[] = "/tmp/etc.XXXXXX";
|
||||||
static bool fs_made = false;
|
static bool fs_made = false;
|
||||||
static char fs[] = "/tmp/fs.XXXXXX";
|
static char fs[] = "/tmp/fs.XXXXXX";
|
||||||
|
static int exit_gui_code = -1;
|
||||||
|
|
||||||
static void unmount_all_but_root(void)
|
static void unmount_all_but_root(void)
|
||||||
{
|
{
|
||||||
|
@ -376,6 +377,14 @@ void exit_handler(void)
|
||||||
rmdir(fs);
|
rmdir(fs);
|
||||||
if ( etc_made )
|
if ( etc_made )
|
||||||
execute((const char*[]) { "rm", "-rf", etc, NULL }, "");
|
execute((const char*[]) { "rm", "-rf", etc, NULL }, "");
|
||||||
|
if ( 0 <= exit_gui_code )
|
||||||
|
gui_shutdown(exit_gui_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit_gui(int code)
|
||||||
|
{
|
||||||
|
exit_gui_code = code;
|
||||||
|
exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cancel_on_sigint(int signum)
|
static void cancel_on_sigint(int signum)
|
||||||
|
@ -544,7 +553,8 @@ int main(void)
|
||||||
|
|
||||||
install_configurationf("upgrade.conf", "a", "src = yes\n");
|
install_configurationf("upgrade.conf", "a", "src = yes\n");
|
||||||
|
|
||||||
bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0);
|
bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0) ||
|
||||||
|
getenv("DISPLAY_SOCKET");
|
||||||
while ( kblayout_setable )
|
while ( kblayout_setable )
|
||||||
{
|
{
|
||||||
// TODO: Detect the name of the current keyboard layout.
|
// TODO: Detect the name of the current keyboard layout.
|
||||||
|
@ -598,38 +608,16 @@ int main(void)
|
||||||
text("\n");
|
text("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tiocgdisplay display;
|
struct dispmsg_crtc_mode mode;
|
||||||
struct tiocgdisplays gdisplays;
|
if ( get_video_mode(&mode) )
|
||||||
memset(&gdisplays, 0, sizeof(gdisplays));
|
|
||||||
gdisplays.count = 1;
|
|
||||||
gdisplays.displays = &display;
|
|
||||||
struct dispmsg_get_driver_name dgdn = { 0 };
|
|
||||||
dgdn.msgid = DISPMSG_GET_DRIVER_NAME;
|
|
||||||
dgdn.device = 0;
|
|
||||||
dgdn.driver_index = 0;
|
|
||||||
dgdn.name.byte_size = 0;
|
|
||||||
dgdn.name.str = NULL;
|
|
||||||
if ( ioctl(1, TIOCGDISPLAYS, &gdisplays) == 0 &&
|
|
||||||
0 < gdisplays.count &&
|
|
||||||
(dgdn.device = display.device, true) &&
|
|
||||||
(dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV) )
|
|
||||||
{
|
{
|
||||||
struct dispmsg_get_crtc_mode get_mode;
|
bool good = (mode.control & DISPMSG_CONTROL_VALID) &&
|
||||||
memset(&get_mode, 0, sizeof(get_mode));
|
(mode.control & DISPMSG_CONTROL_GOOD_DEFAULT);
|
||||||
get_mode.msgid = DISPMSG_GET_CRTC_MODE;
|
if ( mode.control & DISPMSG_CONTROL_VM_AUTO_SCALE )
|
||||||
get_mode.device = 0;
|
|
||||||
get_mode.connector = 0;
|
|
||||||
bool good = false;
|
|
||||||
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) == 0 )
|
|
||||||
{
|
{
|
||||||
good = (get_mode.mode.control & DISPMSG_CONTROL_VALID) &&
|
text("The display resolution will automatically change to "
|
||||||
(get_mode.mode.control & DISPMSG_CONTROL_GOOD_DEFAULT);
|
"match the size of the virtual machine window.\n\n");
|
||||||
if ( get_mode.mode.control & DISPMSG_CONTROL_VM_AUTO_SCALE )
|
good = true;
|
||||||
{
|
|
||||||
text("The display resolution will automatically change to "
|
|
||||||
"match the size of the virtual machine window.\n\n");
|
|
||||||
good = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const char* def = non_interactive || good ? "no" : "yes";
|
const char* def = non_interactive || good ? "no" : "yes";
|
||||||
while ( true )
|
while ( true )
|
||||||
|
@ -648,14 +636,12 @@ int main(void)
|
||||||
if ( execute((const char*[]) { "chvideomode", r, NULL }, "f") != 0 )
|
if ( execute((const char*[]) { "chvideomode", r, NULL }, "f") != 0 )
|
||||||
continue;
|
continue;
|
||||||
input[0] = '\0';
|
input[0] = '\0';
|
||||||
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) < 0 ||
|
if ( !get_video_mode(&mode) ||
|
||||||
!(get_mode.mode.control & DISPMSG_CONTROL_VALID) ||
|
!(mode.control & DISPMSG_CONTROL_VALID) ||
|
||||||
get_mode.mode.control & DISPMSG_CONTROL_VGA )
|
mode.control & DISPMSG_CONTROL_VGA )
|
||||||
break;
|
continue;
|
||||||
snprintf(input, sizeof(input), "%ux%ux%u",
|
snprintf(input, sizeof(input), "%ux%ux%u",
|
||||||
get_mode.mode.view_xres,
|
mode.view_xres, mode.view_yres, mode.fb_format);
|
||||||
get_mode.mode.view_yres,
|
|
||||||
get_mode.mode.fb_format);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -918,7 +904,7 @@ int main(void)
|
||||||
{
|
{
|
||||||
prompt(input, sizeof(input), "confirm_install",
|
prompt(input, sizeof(input), "confirm_install",
|
||||||
"Install " BRAND_DISTRIBUTION_NAME "? "
|
"Install " BRAND_DISTRIBUTION_NAME "? "
|
||||||
"(yes/no/poweroff/reboot/halt)", "yes");
|
"(yes/no/exit/poweroff/reboot/halt)", "yes");
|
||||||
if ( !strcasecmp(input, "yes") )
|
if ( !strcasecmp(input, "yes") )
|
||||||
break;
|
break;
|
||||||
else if ( !strcasecmp(input, "no") )
|
else if ( !strcasecmp(input, "no") )
|
||||||
|
@ -929,12 +915,14 @@ int main(void)
|
||||||
"'halt' to cancel the installation.\n");
|
"'halt' to cancel the installation.\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if ( !strcasecmp(input, "poweroff") )
|
else if ( !strcasecmp(input, "exit") )
|
||||||
exit(0);
|
exit(0);
|
||||||
|
else if ( !strcasecmp(input, "poweroff") )
|
||||||
|
exit_gui(0);
|
||||||
else if ( !strcasecmp(input, "reboot") )
|
else if ( !strcasecmp(input, "reboot") )
|
||||||
exit(1);
|
exit_gui(1);
|
||||||
else if ( !strcasecmp(input, "halt") )
|
else if ( !strcasecmp(input, "halt") )
|
||||||
exit(2);
|
exit_gui(2);
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1293,6 +1281,27 @@ int main(void)
|
||||||
|
|
||||||
// TODO: Ask if networking should be disabled / enabled.
|
// TODO: Ask if networking should be disabled / enabled.
|
||||||
|
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
prompt(input, sizeof(input), "enable_gui",
|
||||||
|
"Enable graphical user interface?",
|
||||||
|
getenv("DISPLAY_SOCKET") ? "yes" : "no");
|
||||||
|
if ( strcasecmp(input, "no") == 0 )
|
||||||
|
break;
|
||||||
|
if ( strcasecmp(input, "yes") != 0 )
|
||||||
|
continue;
|
||||||
|
if ( !install_configurationf("etc/session", "w",
|
||||||
|
"#!sh\nexec display\n") ||
|
||||||
|
chmod("etc/session", 0755) < 0 )
|
||||||
|
{
|
||||||
|
warn("etc/session");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
text("Added 'exec display' to /etc/session\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
text("\n");
|
||||||
|
|
||||||
if ( !access_or_die("/tix/tixinfo/ntpd", F_OK) )
|
if ( !access_or_die("/tix/tixinfo/ntpd", F_OK) )
|
||||||
{
|
{
|
||||||
text("A Network Time Protocol client (ntpd) has been installed that "
|
text("A Network Time Protocol client (ntpd) has been installed that "
|
||||||
|
@ -1538,11 +1547,11 @@ int main(void)
|
||||||
if ( !strcasecmp(input, "exit") )
|
if ( !strcasecmp(input, "exit") )
|
||||||
exit(0);
|
exit(0);
|
||||||
else if ( !strcasecmp(input, "poweroff") )
|
else if ( !strcasecmp(input, "poweroff") )
|
||||||
exit(0);
|
exit_gui(0);
|
||||||
else if ( !strcasecmp(input, "reboot") )
|
else if ( !strcasecmp(input, "reboot") )
|
||||||
exit(1);
|
exit_gui(1);
|
||||||
else if ( !strcasecmp(input, "halt") )
|
else if ( !strcasecmp(input, "halt") )
|
||||||
exit(2);
|
exit_gui(2);
|
||||||
else if ( !strcasecmp(input, "boot") )
|
else if ( !strcasecmp(input, "boot") )
|
||||||
{
|
{
|
||||||
if ( !access("/etc/fstab", F_OK) )
|
if ( !access("/etc/fstab", F_OK) )
|
||||||
|
@ -1557,7 +1566,7 @@ int main(void)
|
||||||
"echo 'require chain exit-code' > "
|
"echo 'require chain exit-code' > "
|
||||||
"/etc/init/default", NULL },
|
"/etc/init/default", NULL },
|
||||||
"ef");
|
"ef");
|
||||||
exit(3);
|
exit_gui(3);
|
||||||
}
|
}
|
||||||
else if ( !strcasecmp(input, "chroot") )
|
else if ( !strcasecmp(input, "chroot") )
|
||||||
{
|
{
|
||||||
|
|
|
@ -73,6 +73,7 @@ static struct mountpoint* mountpoints;
|
||||||
static size_t mountpoints_used;
|
static size_t mountpoints_used;
|
||||||
static bool fs_made = false;
|
static bool fs_made = false;
|
||||||
static char fs[] = "/tmp/fs.XXXXXX";
|
static char fs[] = "/tmp/fs.XXXXXX";
|
||||||
|
static int exit_gui_code = -1;
|
||||||
|
|
||||||
static bool add_installation(struct blockdevice* bdev,
|
static bool add_installation(struct blockdevice* bdev,
|
||||||
struct release* release,
|
struct release* release,
|
||||||
|
@ -329,6 +330,14 @@ void exit_handler(void)
|
||||||
}
|
}
|
||||||
if ( fs_made )
|
if ( fs_made )
|
||||||
rmdir(fs);
|
rmdir(fs);
|
||||||
|
if ( 0 <= exit_gui_code )
|
||||||
|
gui_shutdown(exit_gui_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit_gui(int code)
|
||||||
|
{
|
||||||
|
exit_gui_code = code;
|
||||||
|
exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cancel_on_sigint(int signum)
|
static void cancel_on_sigint(int signum)
|
||||||
|
@ -457,7 +466,8 @@ int main(void)
|
||||||
prompt(input, sizeof(input), "ready", "Ready?", ready);
|
prompt(input, sizeof(input), "ready", "Ready?", ready);
|
||||||
text("\n");
|
text("\n");
|
||||||
|
|
||||||
bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0);
|
bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0) ||
|
||||||
|
getenv("DISPLAY_SOCKET");
|
||||||
while ( kblayout_setable )
|
while ( kblayout_setable )
|
||||||
{
|
{
|
||||||
// TODO: Detect the name of the current keyboard layout.
|
// TODO: Detect the name of the current keyboard layout.
|
||||||
|
@ -499,38 +509,16 @@ int main(void)
|
||||||
if ( kblayout_setable )
|
if ( kblayout_setable )
|
||||||
text("\n");
|
text("\n");
|
||||||
|
|
||||||
struct tiocgdisplay display;
|
struct dispmsg_crtc_mode mode;
|
||||||
struct tiocgdisplays gdisplays;
|
if ( get_video_mode(&mode) )
|
||||||
memset(&gdisplays, 0, sizeof(gdisplays));
|
|
||||||
gdisplays.count = 1;
|
|
||||||
gdisplays.displays = &display;
|
|
||||||
struct dispmsg_get_driver_name dgdn = { 0 };
|
|
||||||
dgdn.msgid = DISPMSG_GET_DRIVER_NAME;
|
|
||||||
dgdn.device = 0;
|
|
||||||
dgdn.driver_index = 0;
|
|
||||||
dgdn.name.byte_size = 0;
|
|
||||||
dgdn.name.str = NULL;
|
|
||||||
if ( ioctl(1, TIOCGDISPLAYS, &gdisplays) == 0 &&
|
|
||||||
0 < gdisplays.count &&
|
|
||||||
(dgdn.device = display.device, true) &&
|
|
||||||
(dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV) )
|
|
||||||
{
|
{
|
||||||
struct dispmsg_get_crtc_mode get_mode;
|
bool good = (mode.control & DISPMSG_CONTROL_VALID) &&
|
||||||
memset(&get_mode, 0, sizeof(get_mode));
|
(mode.control & DISPMSG_CONTROL_GOOD_DEFAULT);
|
||||||
get_mode.msgid = DISPMSG_GET_CRTC_MODE;
|
if ( mode.control & DISPMSG_CONTROL_VM_AUTO_SCALE )
|
||||||
get_mode.device = 0;
|
|
||||||
get_mode.connector = 0;
|
|
||||||
bool good = false;
|
|
||||||
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) == 0 )
|
|
||||||
{
|
{
|
||||||
good = (get_mode.mode.control & DISPMSG_CONTROL_VALID) &&
|
text("The display resolution will automatically change to "
|
||||||
(get_mode.mode.control & DISPMSG_CONTROL_GOOD_DEFAULT);
|
"match the size of the virtual machine window.\n\n");
|
||||||
if ( get_mode.mode.control & DISPMSG_CONTROL_VM_AUTO_SCALE )
|
good = true;
|
||||||
{
|
|
||||||
text("The display resolution will automatically change to "
|
|
||||||
"match the size of the virtual machine window.\n\n");
|
|
||||||
good = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const char* def = non_interactive || good ? "no" : "yes";
|
const char* def = non_interactive || good ? "no" : "yes";
|
||||||
while ( true )
|
while ( true )
|
||||||
|
@ -835,7 +823,7 @@ int main(void)
|
||||||
while ( true )
|
while ( true )
|
||||||
{
|
{
|
||||||
promptx(input, sizeof(input), "confirm_upgrade",
|
promptx(input, sizeof(input), "confirm_upgrade",
|
||||||
"Upgrade? (yes/no/poweroff/reboot/halt)", "yes", true);
|
"Upgrade? (yes/no/exit/poweroff/reboot/halt)", "yes", true);
|
||||||
if ( !strcasecmp(input, "yes") )
|
if ( !strcasecmp(input, "yes") )
|
||||||
break;
|
break;
|
||||||
else if ( !strcasecmp(input, "no") )
|
else if ( !strcasecmp(input, "no") )
|
||||||
|
@ -848,12 +836,14 @@ int main(void)
|
||||||
"'halt' or cancel the upgrade.\n");
|
"'halt' or cancel the upgrade.\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if ( !strcasecmp(input, "poweroff") )
|
else if ( !strcasecmp(input, "exit") )
|
||||||
exit(0);
|
exit(0);
|
||||||
|
else if ( !strcasecmp(input, "poweroff") )
|
||||||
|
exit_gui(0);
|
||||||
else if ( !strcasecmp(input, "reboot") )
|
else if ( !strcasecmp(input, "reboot") )
|
||||||
exit(1);
|
exit_gui(1);
|
||||||
else if ( !strcasecmp(input, "halt") )
|
else if ( !strcasecmp(input, "halt") )
|
||||||
exit(2);
|
exit_gui(2);
|
||||||
else if ( !strcasecmp(input, "!") )
|
else if ( !strcasecmp(input, "!") )
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
|
@ -991,12 +981,14 @@ int main(void)
|
||||||
while ( true )
|
while ( true )
|
||||||
{
|
{
|
||||||
prompt(input, sizeof(input), "finally",
|
prompt(input, sizeof(input), "finally",
|
||||||
"What now? (poweroff/reboot/halt)", "reboot");
|
"What now? (exit/poweroff/reboot/halt)", "reboot");
|
||||||
if ( !strcasecmp(input, "poweroff") )
|
if ( !strcasecmp(input, "exit") )
|
||||||
return 0;
|
exit(0);
|
||||||
if ( !strcasecmp(input, "reboot") )
|
else if ( !strcasecmp(input, "poweroff") )
|
||||||
return 1;
|
exit_gui(0);
|
||||||
if ( !strcasecmp(input, "halt") )
|
else if ( !strcasecmp(input, "reboot") )
|
||||||
return 2;
|
exit_gui(1);
|
||||||
|
else if ( !strcasecmp(input, "halt") )
|
||||||
|
exit_gui(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
terminal
|
||||||
|
*.o
|
|
@ -0,0 +1,36 @@
|
||||||
|
SOFTWARE_MEANT_FOR_SORTIX=1
|
||||||
|
include ../build-aux/platform.mak
|
||||||
|
include ../build-aux/compiler.mak
|
||||||
|
include ../build-aux/dirs.mak
|
||||||
|
|
||||||
|
OPTLEVEL?=-g -O2
|
||||||
|
CFLAGS?=$(OPTLEVEL)
|
||||||
|
|
||||||
|
CFLAGS:=$(CFLAGS) -Wall -Wextra
|
||||||
|
|
||||||
|
PROGRAM=terminal
|
||||||
|
MANPAGES1 = terminal.1
|
||||||
|
|
||||||
|
OBJS=\
|
||||||
|
terminal.o \
|
||||||
|
|
||||||
|
LIBS:=-lui -ldisplay
|
||||||
|
|
||||||
|
all: $(PROGRAM)
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p $(DESTDIR)$(BINDIR)
|
||||||
|
install $(PROGRAM) $(DESTDIR)$(BINDIR)
|
||||||
|
mkdir -p $(DESTDIR)$(MANDIR)/man1
|
||||||
|
install $(MANPAGES1) $(DESTDIR)$(MANDIR)/man1
|
||||||
|
|
||||||
|
$(PROGRAM): $(OBJS)
|
||||||
|
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $(OBJS) -o $@ $(LIBS)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(PROGRAM) *.o
|
|
@ -0,0 +1,283 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* palette.h
|
||||||
|
* Console color palette, matches the xterm palette with tango colors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PALETTE_H
|
||||||
|
#define PALETTE_H
|
||||||
|
|
||||||
|
static const uint32_t palette[256] =
|
||||||
|
{
|
||||||
|
0x000000,
|
||||||
|
0xcc0000,
|
||||||
|
0x3e9a06,
|
||||||
|
0xc4a000,
|
||||||
|
0x3465a4,
|
||||||
|
0x75507b,
|
||||||
|
0x06989a,
|
||||||
|
0xbfbfbf,
|
||||||
|
0x555753,
|
||||||
|
0xef2929,
|
||||||
|
0x8ae234,
|
||||||
|
0xfce94f,
|
||||||
|
0x729fcf,
|
||||||
|
0xad7fa8,
|
||||||
|
0x34e2e2,
|
||||||
|
0xffffff,
|
||||||
|
0x000000,
|
||||||
|
0x00005f,
|
||||||
|
0x000087,
|
||||||
|
0x0000af,
|
||||||
|
0x0000d7,
|
||||||
|
0x0000ff,
|
||||||
|
0x005f00,
|
||||||
|
0x005f5f,
|
||||||
|
0x005f87,
|
||||||
|
0x005faf,
|
||||||
|
0x005fd7,
|
||||||
|
0x005fff,
|
||||||
|
0x008700,
|
||||||
|
0x00875f,
|
||||||
|
0x008787,
|
||||||
|
0x0087af,
|
||||||
|
0x0087d7,
|
||||||
|
0x0087ff,
|
||||||
|
0x00af00,
|
||||||
|
0x00af5f,
|
||||||
|
0x00af87,
|
||||||
|
0x00afaf,
|
||||||
|
0x00afd7,
|
||||||
|
0x00afff,
|
||||||
|
0x00d700,
|
||||||
|
0x00d75f,
|
||||||
|
0x00d787,
|
||||||
|
0x00d7af,
|
||||||
|
0x00d7d7,
|
||||||
|
0x00d7ff,
|
||||||
|
0x00ff00,
|
||||||
|
0x00ff5f,
|
||||||
|
0x00ff87,
|
||||||
|
0x00ffaf,
|
||||||
|
0x00ffd7,
|
||||||
|
0x00ffff,
|
||||||
|
0x5f0000,
|
||||||
|
0x5f005f,
|
||||||
|
0x5f0087,
|
||||||
|
0x5f00af,
|
||||||
|
0x5f00d7,
|
||||||
|
0x5f00ff,
|
||||||
|
0x5f5f00,
|
||||||
|
0x5f5f5f,
|
||||||
|
0x5f5f87,
|
||||||
|
0x5f5faf,
|
||||||
|
0x5f5fd7,
|
||||||
|
0x5f5fff,
|
||||||
|
0x5f8700,
|
||||||
|
0x5f875f,
|
||||||
|
0x5f8787,
|
||||||
|
0x5f87af,
|
||||||
|
0x5f87d7,
|
||||||
|
0x5f87ff,
|
||||||
|
0x5faf00,
|
||||||
|
0x5faf5f,
|
||||||
|
0x5faf87,
|
||||||
|
0x5fafaf,
|
||||||
|
0x5fafd7,
|
||||||
|
0x5fafff,
|
||||||
|
0x5fd700,
|
||||||
|
0x5fd75f,
|
||||||
|
0x5fd787,
|
||||||
|
0x5fd7af,
|
||||||
|
0x5fd7d7,
|
||||||
|
0x5fd7ff,
|
||||||
|
0x5fff00,
|
||||||
|
0x5fff5f,
|
||||||
|
0x5fff87,
|
||||||
|
0x5fffaf,
|
||||||
|
0x5fffd7,
|
||||||
|
0x5fffff,
|
||||||
|
0x870000,
|
||||||
|
0x87005f,
|
||||||
|
0x870087,
|
||||||
|
0x8700af,
|
||||||
|
0x8700d7,
|
||||||
|
0x8700ff,
|
||||||
|
0x875f00,
|
||||||
|
0x875f5f,
|
||||||
|
0x875f87,
|
||||||
|
0x875faf,
|
||||||
|
0x875fd7,
|
||||||
|
0x875fff,
|
||||||
|
0x878700,
|
||||||
|
0x87875f,
|
||||||
|
0x878787,
|
||||||
|
0x8787af,
|
||||||
|
0x8787d7,
|
||||||
|
0x8787ff,
|
||||||
|
0x87af00,
|
||||||
|
0x87af5f,
|
||||||
|
0x87af87,
|
||||||
|
0x87afaf,
|
||||||
|
0x87afd7,
|
||||||
|
0x87afff,
|
||||||
|
0x87d700,
|
||||||
|
0x87d75f,
|
||||||
|
0x87d787,
|
||||||
|
0x87d7af,
|
||||||
|
0x87d7d7,
|
||||||
|
0x87d7ff,
|
||||||
|
0x87ff00,
|
||||||
|
0x87ff5f,
|
||||||
|
0x87ff87,
|
||||||
|
0x87ffaf,
|
||||||
|
0x87ffd7,
|
||||||
|
0x87ffff,
|
||||||
|
0xaf0000,
|
||||||
|
0xaf005f,
|
||||||
|
0xaf0087,
|
||||||
|
0xaf00af,
|
||||||
|
0xaf00d7,
|
||||||
|
0xaf00ff,
|
||||||
|
0xaf5f00,
|
||||||
|
0xaf5f5f,
|
||||||
|
0xaf5f87,
|
||||||
|
0xaf5faf,
|
||||||
|
0xaf5fd7,
|
||||||
|
0xaf5fff,
|
||||||
|
0xaf8700,
|
||||||
|
0xaf875f,
|
||||||
|
0xaf8787,
|
||||||
|
0xaf87af,
|
||||||
|
0xaf87d7,
|
||||||
|
0xaf87ff,
|
||||||
|
0xafaf00,
|
||||||
|
0xafaf5f,
|
||||||
|
0xafaf87,
|
||||||
|
0xafafaf,
|
||||||
|
0xafafd7,
|
||||||
|
0xafafff,
|
||||||
|
0xafd700,
|
||||||
|
0xafd75f,
|
||||||
|
0xafd787,
|
||||||
|
0xafd7af,
|
||||||
|
0xafd7d7,
|
||||||
|
0xafd7ff,
|
||||||
|
0xafff00,
|
||||||
|
0xafff5f,
|
||||||
|
0xafff87,
|
||||||
|
0xafffaf,
|
||||||
|
0xafffd7,
|
||||||
|
0xafffff,
|
||||||
|
0xd70000,
|
||||||
|
0xd7005f,
|
||||||
|
0xd70087,
|
||||||
|
0xd700af,
|
||||||
|
0xd700d7,
|
||||||
|
0xd700ff,
|
||||||
|
0xd75f00,
|
||||||
|
0xd75f5f,
|
||||||
|
0xd75f87,
|
||||||
|
0xd75faf,
|
||||||
|
0xd75fd7,
|
||||||
|
0xd75fff,
|
||||||
|
0xd78700,
|
||||||
|
0xd7875f,
|
||||||
|
0xd78787,
|
||||||
|
0xd787af,
|
||||||
|
0xd787d7,
|
||||||
|
0xd787ff,
|
||||||
|
0xd7af00,
|
||||||
|
0xd7af5f,
|
||||||
|
0xd7af87,
|
||||||
|
0xd7afaf,
|
||||||
|
0xd7afd7,
|
||||||
|
0xd7afff,
|
||||||
|
0xd7d700,
|
||||||
|
0xd7d75f,
|
||||||
|
0xd7d787,
|
||||||
|
0xd7d7af,
|
||||||
|
0xd7d7d7,
|
||||||
|
0xd7d7ff,
|
||||||
|
0xd7ff00,
|
||||||
|
0xd7ff5f,
|
||||||
|
0xd7ff87,
|
||||||
|
0xd7ffaf,
|
||||||
|
0xd7ffd7,
|
||||||
|
0xd7ffff,
|
||||||
|
0xff0000,
|
||||||
|
0xff005f,
|
||||||
|
0xff0087,
|
||||||
|
0xff00af,
|
||||||
|
0xff00d7,
|
||||||
|
0xff00ff,
|
||||||
|
0xff5f00,
|
||||||
|
0xff5f5f,
|
||||||
|
0xff5f87,
|
||||||
|
0xff5faf,
|
||||||
|
0xff5fd7,
|
||||||
|
0xff5fff,
|
||||||
|
0xff8700,
|
||||||
|
0xff875f,
|
||||||
|
0xff8787,
|
||||||
|
0xff87af,
|
||||||
|
0xff87d7,
|
||||||
|
0xff87ff,
|
||||||
|
0xffaf00,
|
||||||
|
0xffaf5f,
|
||||||
|
0xffaf87,
|
||||||
|
0xffafaf,
|
||||||
|
0xffafd7,
|
||||||
|
0xffafff,
|
||||||
|
0xffd700,
|
||||||
|
0xffd75f,
|
||||||
|
0xffd787,
|
||||||
|
0xffd7af,
|
||||||
|
0xffd7d7,
|
||||||
|
0xffd7ff,
|
||||||
|
0xffff00,
|
||||||
|
0xffff5f,
|
||||||
|
0xffff87,
|
||||||
|
0xffffaf,
|
||||||
|
0xffffd7,
|
||||||
|
0xffffff,
|
||||||
|
0x080808,
|
||||||
|
0x121212,
|
||||||
|
0x1c1c1c,
|
||||||
|
0x262626,
|
||||||
|
0x303030,
|
||||||
|
0x3a3a3a,
|
||||||
|
0x444444,
|
||||||
|
0x4e4e4e,
|
||||||
|
0x585858,
|
||||||
|
0x626262,
|
||||||
|
0x6c6c6c,
|
||||||
|
0x767676,
|
||||||
|
0x808080,
|
||||||
|
0x8a8a8a,
|
||||||
|
0x949494,
|
||||||
|
0x9e9e9e,
|
||||||
|
0xa8a8a8,
|
||||||
|
0xb2b2b2,
|
||||||
|
0xbcbcbc,
|
||||||
|
0xc6c6c6,
|
||||||
|
0xd0d0d0,
|
||||||
|
0xdadada,
|
||||||
|
0xe4e4e4,
|
||||||
|
0xeeeeee,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,44 @@
|
||||||
|
.Dd June 17, 2023
|
||||||
|
.Dt TERMINAL 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm terminal
|
||||||
|
.Nd graphical terminal emulator
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Ar command ...
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
is a graphical terminal emulator for the
|
||||||
|
.Xr display 1
|
||||||
|
desktop environment.
|
||||||
|
.Nm
|
||||||
|
has essentially the same features as the
|
||||||
|
.Xr kernel 7
|
||||||
|
console.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar command
|
||||||
|
is executed inside a
|
||||||
|
.Xr pts 4
|
||||||
|
psuedoterminal.
|
||||||
|
A login shell can be requested with a leading hyphen
|
||||||
|
.Sq - .
|
||||||
|
If no command was specified, the user's shell per
|
||||||
|
.Xr passwd 5
|
||||||
|
is run as a login shell with
|
||||||
|
.Xr sh 1
|
||||||
|
as a fallback.
|
||||||
|
.Sh ENVIRONMENT
|
||||||
|
.Bl -tag -width "TERM"
|
||||||
|
.It TERM Ns = Ns Sy sortix
|
||||||
|
The terminal type.
|
||||||
|
.El
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
.Nm
|
||||||
|
will exit 0 on success and non-zero otherwise.
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr display 1 ,
|
||||||
|
.Xr sh 1 ,
|
||||||
|
.Xr pts 4 ,
|
||||||
|
.Xr tty 4
|
File diff suppressed because it is too large
Load Diff
|
@ -23,6 +23,7 @@ default=
|
||||||
directory=
|
directory=
|
||||||
enable_append_title=true
|
enable_append_title=true
|
||||||
enable_dhclient=
|
enable_dhclient=
|
||||||
|
enable_gui=
|
||||||
enable_network_drivers=
|
enable_network_drivers=
|
||||||
enable_ntpd=
|
enable_ntpd=
|
||||||
enable_src=
|
enable_src=
|
||||||
|
@ -56,12 +57,14 @@ for argument do
|
||||||
--default) previous_option=default ;;
|
--default) previous_option=default ;;
|
||||||
--disable-append-title) enable_append_title=false ;;
|
--disable-append-title) enable_append_title=false ;;
|
||||||
--disable-dhclient) enable_dhclient=false ;;
|
--disable-dhclient) enable_dhclient=false ;;
|
||||||
|
--disable-gui) enable_gui=false ;;
|
||||||
--disable-network-drivers) enable_network_drivers=false ;;
|
--disable-network-drivers) enable_network_drivers=false ;;
|
||||||
--disable-ntpd) enable_ntpd=false ;;
|
--disable-ntpd) enable_ntpd=false ;;
|
||||||
--disable-src) enable_src=false ;;
|
--disable-src) enable_src=false ;;
|
||||||
--disable-sshd) enable_sshd=false ;;
|
--disable-sshd) enable_sshd=false ;;
|
||||||
--enable-append-title) enable_append_title=true ;;
|
--enable-append-title) enable_append_title=true ;;
|
||||||
--enable-dhclient) enable_dhclient=true ;;
|
--enable-dhclient) enable_dhclient=true ;;
|
||||||
|
--enable-gui) enable_gui=true ;;
|
||||||
--enable-network-drivers) enable_network_drivers=true ;;
|
--enable-network-drivers) enable_network_drivers=true ;;
|
||||||
--enable-ntpd) enable_ntpd=true ;;
|
--enable-ntpd) enable_ntpd=true ;;
|
||||||
--enable-src) enable_src=true ;;
|
--enable-src) enable_src=true ;;
|
||||||
|
@ -162,6 +165,7 @@ mkdir -p -- "$directory/boot/grub"
|
||||||
echo "title_sysupgrade='***AUTOMATIC UPGRADE***'"
|
echo "title_sysupgrade='***AUTOMATIC UPGRADE***'"
|
||||||
fi
|
fi
|
||||||
print_enable_default_bool "$enable_dhclient" dhclient dhclient
|
print_enable_default_bool "$enable_dhclient" dhclient dhclient
|
||||||
|
print_enable_default_bool "$enable_gui" gui gui
|
||||||
print_enable_default "$enable_network_drivers" network_drivers network-drivers
|
print_enable_default "$enable_network_drivers" network_drivers network-drivers
|
||||||
print_enable_default_bool "$enable_src" src src
|
print_enable_default_bool "$enable_src" src src
|
||||||
print_enable_default_bool "$enable_sshd" sshd sshd
|
print_enable_default_bool "$enable_sshd" sshd sshd
|
||||||
|
|
|
@ -10,12 +10,14 @@
|
||||||
.Op Fl \-default Ns = Ns Ar default-boot-menu-option
|
.Op Fl \-default Ns = Ns Ar default-boot-menu-option
|
||||||
.Op Fl \-disable-append-title
|
.Op Fl \-disable-append-title
|
||||||
.Op Fl \-disable-dhclient
|
.Op Fl \-disable-dhclient
|
||||||
|
.Op Fl \-disable-gui
|
||||||
.Op Fl \-disable-network-drivers
|
.Op Fl \-disable-network-drivers
|
||||||
.Op Fl \-disable-ntpd
|
.Op Fl \-disable-ntpd
|
||||||
.Op Fl \-disable-src
|
.Op Fl \-disable-src
|
||||||
.Op Fl \-disable-sshd
|
.Op Fl \-disable-sshd
|
||||||
.Op Fl \-enable-append-title
|
.Op Fl \-enable-append-title
|
||||||
.Op Fl \-enable-dhclient
|
.Op Fl \-enable-dhclient
|
||||||
|
.Op Fl \-enable-gui
|
||||||
.Op Fl \-enable-network-drivers
|
.Op Fl \-enable-network-drivers
|
||||||
.Op Fl \-enable-ntpd
|
.Op Fl \-enable-ntpd
|
||||||
.Op Fl \-enable-src
|
.Op Fl \-enable-src
|
||||||
|
@ -105,6 +107,16 @@ GRUB variable to
|
||||||
causing the bootloader to load additional configuration that turns off the
|
causing the bootloader to load additional configuration that turns off the
|
||||||
.Xr dhclient 8
|
.Xr dhclient 8
|
||||||
daemon on boot.
|
daemon on boot.
|
||||||
|
.It Fl \-disable-gui
|
||||||
|
Disable the GUI by setting the
|
||||||
|
.Sy enable_gui
|
||||||
|
GRUB variable to
|
||||||
|
.Sy false ,
|
||||||
|
which makes the bootloader configuration not append
|
||||||
|
.Sy -gui
|
||||||
|
to the requested
|
||||||
|
.Xr init 8
|
||||||
|
target.
|
||||||
.It Fl \-disable-network-drivers
|
.It Fl \-disable-network-drivers
|
||||||
Disable network drivers by setting the
|
Disable network drivers by setting the
|
||||||
.Sy enable_network_drivers
|
.Sy enable_network_drivers
|
||||||
|
@ -152,6 +164,16 @@ GRUB variable to
|
||||||
selecting the default behavior of starting the
|
selecting the default behavior of starting the
|
||||||
.Xr dhclient 8
|
.Xr dhclient 8
|
||||||
daemon.
|
daemon.
|
||||||
|
.It Fl \-enable-gui
|
||||||
|
Enable the GUI by setting the
|
||||||
|
.Sy enable_gui
|
||||||
|
GRUB variable to
|
||||||
|
.Sy true ,
|
||||||
|
which makes the bootloader configuration append
|
||||||
|
.Sy -gui
|
||||||
|
to the requested
|
||||||
|
.Xr init 8
|
||||||
|
target.
|
||||||
.It Fl \-enable-network-drivers
|
.It Fl \-enable-network-drivers
|
||||||
Enable network drivers by setting the
|
Enable network drivers by setting the
|
||||||
.Sy enable_network_drivers
|
.Sy enable_network_drivers
|
||||||
|
@ -350,6 +372,12 @@ automatically using the SSH configuration found in the liveconfig directory:
|
||||||
tix-iso-bootconfig --liveconfig=liveconfig --enable-sshd bootconfig
|
tix-iso-bootconfig --liveconfig=liveconfig --enable-sshd bootconfig
|
||||||
tix-iso-add sortix.iso bootconfig
|
tix-iso-add sortix.iso bootconfig
|
||||||
.Ed
|
.Ed
|
||||||
|
.Ss Boot to Console Instead of GUI By Default
|
||||||
|
To customize a release so it boots to a console instead of the GUI:
|
||||||
|
.Bd -literal
|
||||||
|
tix-iso-bootconfig --disable-gui bootconfig
|
||||||
|
tix-iso-add sortix.iso bootconfig
|
||||||
|
.Ed
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr xorriso 1 ,
|
.Xr xorriso 1 ,
|
||||||
.Xr kernel 7 ,
|
.Xr kernel 7 ,
|
||||||
|
|
|
@ -11,11 +11,9 @@ CXXFLAGS:=$(CXXFLAGS) -std=gnu++11 -Wall -Wextra -fno-exceptions -fno-rtti -fche
|
||||||
|
|
||||||
BINARY:=trianglix
|
BINARY:=trianglix
|
||||||
|
|
||||||
LIBS:=-ldispd
|
|
||||||
|
|
||||||
all: $(BINARY)
|
all: $(BINARY)
|
||||||
|
|
||||||
.PHONY: all install uninstall clean
|
.PHONY: all install clean
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
mkdir -p $(DESTDIR)$(BINDIR)
|
mkdir -p $(DESTDIR)$(BINDIR)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2014, 2015, 2016, 2018 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2013, 2014, 2015, 2016, 2018, 2013 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <dispd.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <error.h>
|
#include <error.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -892,13 +892,6 @@ public:
|
||||||
buffer(buffer), pitch(pitch), xres(xres), yres(yres) { }
|
buffer(buffer), pitch(pitch), xres(xres), yres(yres) { }
|
||||||
FrameBufferInfo(const FrameBufferInfo& o) :
|
FrameBufferInfo(const FrameBufferInfo& o) :
|
||||||
buffer(o.buffer), pitch(o.pitch), xres(o.xres), yres(o.yres) { }
|
buffer(o.buffer), pitch(o.pitch), xres(o.xres), yres(o.yres) { }
|
||||||
FrameBufferInfo(struct dispd_framebuffer* fb)
|
|
||||||
{
|
|
||||||
buffer = dispd_get_framebuffer_data(fb);
|
|
||||||
pitch = dispd_get_framebuffer_pitch(fb);
|
|
||||||
xres = dispd_get_framebuffer_width(fb);
|
|
||||||
yres = dispd_get_framebuffer_height(fb);
|
|
||||||
}
|
|
||||||
~FrameBufferInfo() { }
|
~FrameBufferInfo() { }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -1414,14 +1407,45 @@ static void Render(FrameBufferInfo fb, struct Desktop* desktop)
|
||||||
RenderBackground(fb, desktop);
|
RenderBackground(fb, desktop);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool Render(struct dispd_window* window, struct RenderInfo* info)
|
static bool Render(struct RenderInfo* info)
|
||||||
{
|
{
|
||||||
struct dispd_framebuffer* fb = dispd_begin_render(window);
|
struct dispmsg_get_crtc_mode get_mode_msg;
|
||||||
if ( !fb )
|
memset(&get_mode_msg, 0, sizeof(get_mode_msg));
|
||||||
return false;
|
get_mode_msg.msgid = DISPMSG_GET_CRTC_MODE;
|
||||||
FrameBufferInfo fbinfo(fb);
|
get_mode_msg.device = 0; // TODO: Multi-screen support!
|
||||||
|
get_mode_msg.connector = 0; // TODO: Multi-screen support!
|
||||||
|
if ( dispmsg_issue(&get_mode_msg, sizeof(get_mode_msg)) != 0 )
|
||||||
|
err(1, "dispmsg_issue: dispmsg_get_crtc_mode");
|
||||||
|
struct dispmsg_crtc_mode mode = get_mode_msg.mode;
|
||||||
|
|
||||||
|
if ( !(mode.control & DISPMSG_CONTROL_VALID) )
|
||||||
|
errx(1, "No valid video mode was set");
|
||||||
|
if ( mode.control & DISPMSG_CONTROL_VGA )
|
||||||
|
errx(1, "A VGA text mode was set");
|
||||||
|
if ( mode.fb_format != 32 )
|
||||||
|
errx(1, "A 32-bit video mode wasn't set");
|
||||||
|
|
||||||
|
size_t pitch = 4 * mode.view_xres;
|
||||||
|
size_t framebuffer_size = pitch * mode.view_yres;
|
||||||
|
uint8_t* framebuffer = (uint8_t*) malloc(framebuffer_size);
|
||||||
|
if ( !framebuffer )
|
||||||
|
err(1, "malloc");
|
||||||
|
|
||||||
|
FrameBufferInfo fbinfo(framebuffer, pitch, mode.view_xres, mode.view_yres);
|
||||||
Render(fbinfo, &info->desktop);
|
Render(fbinfo, &info->desktop);
|
||||||
dispd_finish_render(fb);
|
|
||||||
|
struct dispmsg_write_memory write_memory_msg;
|
||||||
|
memset(&write_memory_msg, 0, sizeof(write_memory_msg));
|
||||||
|
write_memory_msg.msgid = DISPMSG_WRITE_MEMORY;
|
||||||
|
write_memory_msg.device = get_mode_msg.device;
|
||||||
|
write_memory_msg.offset = get_mode_msg.connector;
|
||||||
|
write_memory_msg.size = framebuffer_size;
|
||||||
|
write_memory_msg.src = framebuffer;
|
||||||
|
if ( dispmsg_issue(&write_memory_msg, sizeof(write_memory_msg)) != 0 )
|
||||||
|
err(1, "dispmsg_issue: dispmsg_write_memory");
|
||||||
|
|
||||||
|
free(framebuffer);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1815,7 +1839,7 @@ static void HandleEvents(int kbfd, struct Desktop* desktop)
|
||||||
desktop->text_angle += text_speed * delta;
|
desktop->text_angle += text_speed * delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int MainLoop(int argc, char* argv[], int kbfd, struct dispd_window* window)
|
static int MainLoop(int argc, char* argv[], int kbfd)
|
||||||
{
|
{
|
||||||
(void) argc;
|
(void) argc;
|
||||||
(void) argv;
|
(void) argv;
|
||||||
|
@ -1824,7 +1848,7 @@ static int MainLoop(int argc, char* argv[], int kbfd, struct dispd_window* windo
|
||||||
for ( info.frame = 0; true; info.frame++ )
|
for ( info.frame = 0; true; info.frame++ )
|
||||||
{
|
{
|
||||||
HandleEvents(kbfd, &info.desktop);
|
HandleEvents(kbfd, &info.desktop);
|
||||||
if ( !Render(window, &info) )
|
if ( !Render(&info) )
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1873,27 +1897,13 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
memcpy(font + 128 * 16, rune_font, sizeof(rune_font));
|
memcpy(font + 128 * 16, rune_font, sizeof(rune_font));
|
||||||
|
|
||||||
if ( !dispd_initialize(&argc, &argv) )
|
|
||||||
error(1, 0, "couldn't initialize dispd library");
|
|
||||||
struct dispd_session* session = dispd_attach_default_session();
|
|
||||||
if ( !session )
|
|
||||||
error(1, 0, "couldn't attach to dispd default session");
|
|
||||||
if ( !dispd_session_setup_game_rgba(session) )
|
|
||||||
error(1, 0, "couldn't setup dispd rgba session");
|
|
||||||
struct dispd_window* window = dispd_create_window_game_rgba(session);
|
|
||||||
if ( !window )
|
|
||||||
error(1, 0, "couldn't create dispd rgba window");
|
|
||||||
|
|
||||||
int kbfd = CreateKeyboardConnection();
|
int kbfd = CreateKeyboardConnection();
|
||||||
if ( kbfd < 0 )
|
if ( kbfd < 0 )
|
||||||
error(1, 0, "couldn't create keyboard connection");
|
error(1, 0, "couldn't create keyboard connection");
|
||||||
|
|
||||||
int ret = MainLoop(argc, argv, kbfd, window);
|
int ret = MainLoop(argc, argv, kbfd);
|
||||||
|
|
||||||
close(kbfd);
|
close(kbfd);
|
||||||
|
|
||||||
dispd_destroy_window(window);
|
|
||||||
dispd_detach_session(session);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,10 @@
|
||||||
|
|
||||||
static void suggest_logout(void)
|
static void suggest_logout(void)
|
||||||
{
|
{
|
||||||
fprintf(stderr, " Exiting your shell normally to logout.\n");
|
if ( getenv("DISPLAY_SOCKET") )
|
||||||
|
fprintf(stderr, " Pressing Ctrl-Alt-Del to exit desktop.\n");
|
||||||
|
else
|
||||||
|
fprintf(stderr, " Exiting your shell normally to logout.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
enum category
|
enum category
|
||||||
|
|
Loading…
Reference in New Issue