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=\
|
||||
libc \
|
||||
libm \
|
||||
dispd \
|
||||
libdisplay \
|
||||
libmount \
|
||||
libui \
|
||||
bench \
|
||||
carray \
|
||||
checksum \
|
||||
|
@ -15,6 +16,7 @@ chkblayout \
|
|||
chvideomode \
|
||||
dhclient \
|
||||
disked \
|
||||
display \
|
||||
dnsconfig \
|
||||
editor \
|
||||
ext \
|
||||
|
@ -32,6 +34,7 @@ rw \
|
|||
sf \
|
||||
sh \
|
||||
sysinstall \
|
||||
terminal \
|
||||
tix \
|
||||
trianglix \
|
||||
update-initrd \
|
||||
|
|
|
@ -198,6 +198,7 @@ fi
|
|||
set enable_src=true
|
||||
set enable_network_drivers=
|
||||
set enable_dhclient=true
|
||||
set enable_gui=true
|
||||
set enable_ntpd=false
|
||||
set enable_sshd=false
|
||||
|
||||
|
@ -214,6 +215,7 @@ export no_random_seed
|
|||
export enable_src
|
||||
export enable_network_drivers
|
||||
export enable_dhclient
|
||||
export enable_gui
|
||||
export enable_ntpd
|
||||
export enable_sshd
|
||||
EOF
|
||||
|
@ -402,10 +404,11 @@ menuentry() {
|
|||
echo
|
||||
printf "menuentry \"Sortix (%s)\" {\n" "$1"
|
||||
if [ -n "$2" ]; then
|
||||
printf " load_sortix %s\n" "$2"
|
||||
#printf " load_sortix '"
|
||||
#printf '%s' "$2" | sed "s,','\\'',g"
|
||||
#printf "'\n"
|
||||
printf " if \$enable_gui; then\n"
|
||||
printf " load_sortix %s-gui\n" "$2"
|
||||
printf " else\n"
|
||||
printf " load_sortix %s\n" "$2"
|
||||
printf " fi\n"
|
||||
else
|
||||
printf " load_sortix\n"
|
||||
fi
|
||||
|
@ -418,7 +421,7 @@ menu_title="\$base_menu_title"
|
|||
hook_menu_pre
|
||||
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_sysupgrade" '-- /sbin/init --target=sysupgrade'
|
||||
|
||||
|
@ -446,6 +449,18 @@ menu_title="\$base_menu_title - Advanced Options"
|
|||
|
||||
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
|
||||
menuentry "Disable loading source code" {
|
||||
enable_src=false
|
||||
|
|
|
@ -151,6 +151,7 @@ EOF
|
|||
tix-iso-bootconfig \
|
||||
--random-seed \
|
||||
--timeout=0 \
|
||||
--disable-gui \
|
||||
--liveconfig=liveconfig \
|
||||
bootconfig
|
||||
mkdir -p bootconfig/boot/grub
|
||||
|
|
|
@ -11,7 +11,7 @@ CFLAGS += -Wall -Wextra
|
|||
BINARIES = chkblayout
|
||||
MANPAGES1 = chkblayout.1
|
||||
|
||||
LIBS =
|
||||
LIBS = -ldisplay
|
||||
|
||||
all: $(BINARIES)
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <display.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
|
@ -34,6 +35,20 @@
|
|||
#include <unistd.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[])
|
||||
{
|
||||
bool list = false;
|
||||
|
@ -103,8 +118,29 @@ int main(int argc, char* argv[])
|
|||
err(1, "read: %s", kblayout_path);
|
||||
close(kblayout_fd);
|
||||
|
||||
if ( tcsetblob(tty_fd, "kblayout", kblayout, kblayout_size) < 0 )
|
||||
err(1, "tcsetblob: kblayout: %s:", kblayout_path);
|
||||
if ( getenv("DISPLAY_SOCKET") )
|
||||
{
|
||||
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);
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ CFLAGS += -Wall -Wextra
|
|||
BINARIES = chvideomode
|
||||
MANPAGES1 = chvideomode.1
|
||||
|
||||
LIBS =
|
||||
LIBS = -ldisplay
|
||||
|
||||
all: $(BINARIES)
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <display.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -37,7 +39,22 @@
|
|||
#include <termios.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)
|
||||
{
|
||||
|
@ -48,6 +65,88 @@ static void restore_terminal(int 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,
|
||||
struct dispmsg_crtc_mode mode)
|
||||
{
|
||||
|
@ -61,7 +160,7 @@ static bool set_current_mode(const struct tiocgdisplay* display,
|
|||
|
||||
static struct dispmsg_crtc_mode*
|
||||
get_available_modes(const struct tiocgdisplay* display,
|
||||
size_t* num_modes_ptr)
|
||||
size_t* modes_count_ptr)
|
||||
{
|
||||
struct dispmsg_get_crtc_modes msg;
|
||||
msg.msgid = DISPMSG_GET_CRTC_MODES;
|
||||
|
@ -70,15 +169,15 @@ get_available_modes(const struct tiocgdisplay* display,
|
|||
size_t guess = 1;
|
||||
while ( true )
|
||||
{
|
||||
struct dispmsg_crtc_mode* ret = (struct dispmsg_crtc_mode*)
|
||||
malloc(sizeof(struct dispmsg_crtc_mode) * guess);
|
||||
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 )
|
||||
{
|
||||
*num_modes_ptr = guess;
|
||||
*modes_count_ptr = guess;
|
||||
return 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,
|
||||
size_t* num_modes_ptr,
|
||||
size_t* modes_count_ptr,
|
||||
struct filter* filter)
|
||||
{
|
||||
size_t in_num = *num_modes_ptr;
|
||||
size_t out_num = 0;
|
||||
for ( size_t i = 0; i < in_num; i++ )
|
||||
size_t in_count = *modes_count_ptr;
|
||||
size_t out_count = 0;
|
||||
for ( size_t i = 0; i < in_count; i++ )
|
||||
{
|
||||
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,
|
||||
size_t num_modes,
|
||||
size_t modes_count,
|
||||
unsigned int xres,
|
||||
unsigned int yres,
|
||||
unsigned int bpp,
|
||||
|
@ -168,7 +267,7 @@ static bool get_mode(struct dispmsg_crtc_mode* modes,
|
|||
bool found_other = false;
|
||||
size_t index;
|
||||
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 &&
|
||||
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,
|
||||
size_t num_modes,
|
||||
size_t modes_count,
|
||||
int mode_set_error,
|
||||
struct dispmsg_crtc_mode* mode)
|
||||
{
|
||||
if ( !isatty(0) )
|
||||
errx(1, "Interactive menu requires stdin to be a terminal");
|
||||
|
||||
int num_modes_display_length = 1;
|
||||
for ( size_t i = num_modes; 10 <= i; i /= 10 )
|
||||
num_modes_display_length++;
|
||||
int modes_count_display_length = 1;
|
||||
for ( size_t i = modes_count; 10 <= i; i /= 10 )
|
||||
modes_count_display_length++;
|
||||
|
||||
size_t selection = 0;
|
||||
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 page = selection / 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;
|
||||
if ( how_many_available < how_many )
|
||||
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";
|
||||
printf("%s", color);
|
||||
printf("\e[2K");
|
||||
printf(" [%-*zu] ", num_modes_display_length, index);
|
||||
printf(" [%-*zu] ", modes_count_display_length, index);
|
||||
if ( modes[index].control & DISPMSG_CONTROL_VALID )
|
||||
printf("%ux%ux%u",
|
||||
modes[index].view_xres,
|
||||
|
@ -325,12 +424,12 @@ static bool select_mode(struct dispmsg_crtc_mode* modes,
|
|||
if ( selection )
|
||||
selection--;
|
||||
else
|
||||
selection = num_modes - 1;
|
||||
selection = modes_count - 1;
|
||||
redraw = true;
|
||||
}
|
||||
else if ( length == 1 && byte == 'B' ) // Down key
|
||||
{
|
||||
if ( selection + 1 == num_modes )
|
||||
if ( selection + 1 == modes_count )
|
||||
selection = 0;
|
||||
else
|
||||
selection++;
|
||||
|
@ -343,7 +442,7 @@ static bool select_mode(struct dispmsg_crtc_mode* modes,
|
|||
else if ( '0' <= byte && byte <= '9' )
|
||||
{
|
||||
uint32_t requested = byte - '0';
|
||||
if ( requested < num_modes )
|
||||
if ( requested < modes_count )
|
||||
{
|
||||
selection = requested;
|
||||
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 tiocgdisplays gdisplays = {0};
|
||||
gdisplays.count = 1;
|
||||
gdisplays.displays = &display;
|
||||
if ( ioctl(1, TIOCGDISPLAYS, &gdisplays) < 0 || gdisplays.count == 0 )
|
||||
if ( use_display )
|
||||
{
|
||||
fprintf(stderr, "No video devices are associated with this terminal.\n");
|
||||
exit(13);
|
||||
connection = display_connect_default();
|
||||
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 )
|
||||
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, "Try make sure a device driver exists and is "
|
||||
|
@ -533,8 +647,8 @@ int main(int argc, char* argv[])
|
|||
exit(11);
|
||||
}
|
||||
|
||||
filter_modes(modes, &num_modes, &filter);
|
||||
if ( !num_modes )
|
||||
filter_modes(modes, &modes_count, &filter);
|
||||
if ( !modes_count )
|
||||
{
|
||||
fprintf(stderr, "No video mode remains after filtering away unwanted "
|
||||
"modes.\n");
|
||||
|
@ -552,10 +666,15 @@ int main(int argc, char* argv[])
|
|||
errx(1, "Invalid video mode: %s", argv[optind]);
|
||||
|
||||
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]);
|
||||
|
||||
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",
|
||||
(uintmax_t) mode.view_xres,
|
||||
(uintmax_t) mode.view_yres,
|
||||
|
@ -568,10 +687,15 @@ int main(int argc, char* argv[])
|
|||
while ( !mode_set )
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
* 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
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* window.h
|
||||
* Handles windows.
|
||||
* damage-rect.c
|
||||
* Damage rectangles.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_DISPD_WINDOW_H
|
||||
#define INCLUDE_DISPD_WINDOW_H
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "damage-rect.h"
|
||||
|
||||
struct dispd_window
|
||||
struct damage_rect damage_rect_add(struct damage_rect a, struct damage_rect b)
|
||||
{
|
||||
struct dispd_session* session;
|
||||
uint8_t* cached_buffer;
|
||||
size_t cached_buffer_size;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
if ( !a.width || !a.height )
|
||||
return b;
|
||||
if ( !b.width || !b.height )
|
||||
return a;
|
||||
if ( a.left < b.left )
|
||||
b.width += b.left - a.left, b.left = a.left;
|
||||
if ( b.width < a.width )
|
||||
b.width = a.width;
|
||||
if ( a.top < b.top )
|
||||
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
|
||||
* 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
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* library.c
|
||||
* Main entry point of the Sortix Display Daemon.
|
||||
* damage-rect.h
|
||||
* Damage rectangles.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#ifndef DAMAGE_RECT_H
|
||||
#define DAMAGE_RECT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <dispd.h>
|
||||
|
||||
#include "session.h"
|
||||
|
||||
bool dispd_initialize(int* argc, char*** argv)
|
||||
struct damage_rect
|
||||
{
|
||||
if ( !dispd__session_initialize(argc, argv) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
size_t left;
|
||||
size_t top;
|
||||
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 \
|
||||
aquatinspitz \
|
||||
|
||||
LIBS:=-ldispd
|
||||
LIBS:=-ldisplay
|
||||
|
||||
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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -22,6 +22,7 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <math.h>
|
||||
|
@ -34,12 +35,15 @@
|
|||
#include <timespec.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <dispd.h>
|
||||
#include <display.h>
|
||||
|
||||
// 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 size_t game_width = 1280;
|
||||
static size_t game_height = 720;
|
||||
static size_t game_width = 800;
|
||||
static size_t game_height = 512;
|
||||
#define MAX_KEY_NUMBER 512
|
||||
static bool keys_down[MAX_KEY_NUMBER];
|
||||
static bool keys_pending[MAX_KEY_NUMBER];
|
||||
|
@ -200,20 +204,16 @@ void update(float deltatime)
|
|||
}
|
||||
|
||||
// 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);
|
||||
if ( !window_fb )
|
||||
{
|
||||
error(0, 0, "unable to begin rendering dispd window");
|
||||
game_running = false;
|
||||
return;
|
||||
}
|
||||
size_t old_framesize = framesize;
|
||||
|
||||
uint32_t* fb = (uint32_t*) dispd_get_framebuffer_data(window_fb);
|
||||
size_t xres = dispd_get_framebuffer_width(window_fb);
|
||||
size_t yres = dispd_get_framebuffer_height(window_fb);
|
||||
size_t pitch = dispd_get_framebuffer_pitch(window_fb) / sizeof(uint32_t);
|
||||
size_t xres = game_width;
|
||||
size_t yres = game_height;
|
||||
size_t pitch = xres;
|
||||
framesize = xres * yres * sizeof(uint32_t);
|
||||
if ( old_framesize != framesize && !(fb = realloc(fb, framesize)) )
|
||||
err(1, "malloc");
|
||||
|
||||
// Render a colorful background.
|
||||
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.
|
||||
|
@ -310,50 +312,68 @@ bool pop_is_key_just_down(int abskbkey)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Read input from the keyboard.
|
||||
void input(void)
|
||||
// When the connection to the display server has disconnected.
|
||||
void on_disconnect(void* ctx)
|
||||
{
|
||||
// Read the keyboard input from the user.
|
||||
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 ( MAX_KEY_NUMBER <= (size_t) abskbkey )
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
(void) ctx;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// When the window is asked to quit.
|
||||
void on_quit(void* ctx, uint32_t window_id)
|
||||
{
|
||||
(void) ctx;
|
||||
(void) window_id;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// When the window has been resized.
|
||||
void on_resize(void* ctx, uint32_t window_id, uint32_t width, uint32_t height)
|
||||
{
|
||||
(void) ctx;
|
||||
if ( window_id != window_id )
|
||||
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.
|
||||
void mainloop(struct dispd_window* window)
|
||||
void mainloop(struct display_connection* connection)
|
||||
{
|
||||
struct dispd_framebuffer* window_fb = dispd_begin_render(window);
|
||||
if ( window_fb )
|
||||
{
|
||||
game_width = dispd_get_framebuffer_width(window_fb);
|
||||
game_height = dispd_get_framebuffer_height(window_fb);
|
||||
dispd_finish_render(window_fb);
|
||||
}
|
||||
struct display_event_handlers handlers = {0};
|
||||
handlers.disconnect_handler = on_disconnect;
|
||||
handlers.quit_handler = on_quit;
|
||||
handlers.resize_handler = on_resize;
|
||||
handlers.keyboard_handler = on_keyboard;
|
||||
|
||||
init();
|
||||
|
||||
struct timespec last_frame_time;
|
||||
clock_gettime(CLOCK_MONOTONIC, &last_frame_time);
|
||||
|
||||
render(window);
|
||||
render(connection);
|
||||
|
||||
while ( game_running )
|
||||
{
|
||||
|
||||
struct timespec current_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);
|
||||
float deltatime = deltatime_ts.tv_sec + deltatime_ts.tv_nsec / 1E9f;
|
||||
|
||||
input();
|
||||
while ( display_poll_event(connection, &handlers) == 0 );
|
||||
|
||||
update(deltatime);
|
||||
render(window);
|
||||
render(connection);
|
||||
|
||||
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.
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if ( !isatty(0) )
|
||||
error(1, errno, "standard input");
|
||||
if ( tcgetattr(0, &saved_tio) < 0 )
|
||||
error(1, errno, "tcsetattr: standard input");
|
||||
if ( atexit(restore_terminal_on_exit) != 0 )
|
||||
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);
|
||||
struct display_connection* connection = display_connect_default();
|
||||
if ( !connection && errno == ECONNREFUSED )
|
||||
display_spawn(argc, argv);
|
||||
if ( !connection )
|
||||
error(1, errno, "Could not connect to display server");
|
||||
|
||||
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");
|
||||
display_create_window(connection, window_id);
|
||||
display_resize_window(connection, window_id, game_width, game_height);
|
||||
display_title_window(connection, window_id, "Aquatinspitz");
|
||||
|
||||
mainloop(window);
|
||||
mainloop(connection);
|
||||
|
||||
dispd_destroy_window(window);
|
||||
dispd_detach_session(session);
|
||||
display_disconnect(connection);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <sys/termmode.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -36,7 +37,11 @@
|
|||
#include <timespec.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()
|
||||
{
|
||||
|
@ -60,7 +65,7 @@ static inline float RandomAngle()
|
|||
|
||||
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;
|
||||
|
@ -110,31 +115,6 @@ bool pop_is_key_just_down(int abskbkey)
|
|||
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 yres;
|
||||
static size_t bpp;
|
||||
|
@ -1143,25 +1123,88 @@ void Render()
|
|||
obj->Render();
|
||||
}
|
||||
}
|
||||
void RunFrame(struct dispd_window* window)
|
||||
|
||||
void on_disconnect(void*)
|
||||
{
|
||||
struct dispd_framebuffer* fb = dispd_begin_render(window);
|
||||
if ( !fb )
|
||||
{
|
||||
error(0, 0, "unable to begin rendering dispd window");
|
||||
gamerunning = false;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void on_quit(void*, uint32_t)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void on_resize(void*, uint32_t window_id, uint32_t width, uint32_t height)
|
||||
{
|
||||
if ( window_id != WINDOW_ID )
|
||||
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();
|
||||
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()
|
||||
|
@ -1172,64 +1215,24 @@ void InitGame()
|
|||
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[])
|
||||
{
|
||||
if ( !isatty(0) )
|
||||
error(1, errno, "standard input");
|
||||
if ( tcgetattr(0, &saved_tio) < 0 )
|
||||
error(1, errno, "tcsetattr: standard input");
|
||||
if ( atexit(restore_terminal_on_exit) != 0 )
|
||||
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);
|
||||
struct display_connection* connection = display_connect_default();
|
||||
if ( !connection && errno == ECONNREFUSED )
|
||||
display_spawn(argc, argv);
|
||||
if ( !connection )
|
||||
error(1, errno, "Could not connect to display server");
|
||||
|
||||
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");
|
||||
display_create_window(connection, WINDOW_ID);
|
||||
display_resize_window(connection, WINDOW_ID, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
display_title_window(connection, WINDOW_ID, "Asteroids");
|
||||
|
||||
InitGame();
|
||||
gamerunning = true;
|
||||
for ( framenum = 0; gamerunning; framenum++ )
|
||||
RunFrame(window);
|
||||
RunFrame(connection);
|
||||
|
||||
dispd_destroy_window(window);
|
||||
dispd_detach_session(session);
|
||||
display_disconnect(connection);
|
||||
|
||||
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
|
||||
* 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
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* framebuffer.h
|
||||
* Keeps track of framebuffers.
|
||||
* vgafont.h
|
||||
* VGA font rendering.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_DISPD_FRAMEBUFFER_H
|
||||
#define INCLUDE_DISPD_FRAMEBUFFER_H
|
||||
#ifndef VGAFONT_H
|
||||
#define VGAFONT_H
|
||||
|
||||
struct dispd_framebuffer
|
||||
{
|
||||
struct dispd_window* window;
|
||||
uint8_t* data;
|
||||
size_t datasize;
|
||||
size_t pitch;
|
||||
int bpp;
|
||||
int width;
|
||||
int height;
|
||||
uint64_t fb_location;
|
||||
};
|
||||
#include <stdint.h>
|
||||
|
||||
#include "framebuffer.h"
|
||||
|
||||
#define FONT_REALWIDTH 8
|
||||
#define FONT_WIDTH 9
|
||||
#define FONT_HEIGHT 16
|
||||
#define FONT_CHARSIZE (FONT_REALWIDTH * FONT_HEIGHT / 8)
|
||||
#define FONT_NUMCHARS 256
|
||||
|
||||
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
|
|
@ -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
|
||||
* 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
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* session.h
|
||||
* Handles session management.
|
||||
* pixel.c
|
||||
* Pixel functions.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_DISPD_SESSION_H
|
||||
#define INCLUDE_DISPD_SESSION_H
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "pixel.h"
|
||||
|
||||
struct dispd_session
|
||||
uint32_t blend_pixel(uint32_t bg_value, uint32_t fg_value)
|
||||
{
|
||||
size_t refcount;
|
||||
uint64_t device;
|
||||
uint64_t connector;
|
||||
struct dispd_window* current_window;
|
||||
bool is_rgba;
|
||||
};
|
||||
|
||||
bool dispd__session_initialize(int* argc, char*** argv);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
union color_rgba8 fg; fg.value = fg_value;
|
||||
union color_rgba8 bg; bg.value = bg_value;
|
||||
if ( fg.a == 255 )
|
||||
return fg.value;
|
||||
if ( fg.a == 0 )
|
||||
return bg.value;
|
||||
union color_rgba8 ret;
|
||||
ret.a = 255;
|
||||
ret.r = ((255-fg.a)*bg.r + fg.a*fg.r) / 256;
|
||||
ret.g = ((255-fg.a)*bg.g + fg.a*fg.g) / 256;
|
||||
ret.b = ((255-fg.a)*bg.b + fg.a*fg.b) / 256;
|
||||
return ret.value;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
union c { struct { uint8_t b; uint8_t g; uint8_t r; }; uint32_t v; };
|
||||
|
||||
static void render_background(struct framebuffer fb)
|
||||
{
|
||||
#if 0
|
||||
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 x = 0; x < fb.xres; x++ )
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
--- libSDL.upstream/make.sortix
|
||||
+++ libSDL/make.sortix
|
||||
|
@ -10,3 +33,358 @@ diff -Paur --no-dereference -- libSDL.upstream/make.sortix libSDL/make.sortix
|
|||
+if [ "$1" = "install" ]; then
|
||||
+rm -fv "$DESTDIR/$EXEC_PREFIX/bin/sdl-config"
|
||||
+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?
|
||||
.It Sy empty_password Ns "=" Ns Oo Sy no "|" Sy yes Oc ( default Sy no )
|
||||
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 )
|
||||
Automatically get time from the network using
|
||||
.Xr ntpd 8 ?
|
||||
|
|
|
@ -116,6 +116,16 @@ It depends on the
|
|||
and
|
||||
.Sy local
|
||||
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
|
||||
Starts the operating system installer.
|
||||
This foreground daemon starts the
|
||||
|
@ -129,6 +139,16 @@ It depends on the
|
|||
and
|
||||
.Sy local
|
||||
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
|
||||
Starts the operating system upgrader.
|
||||
This foreground daemon starts the
|
||||
|
@ -142,6 +162,16 @@ It depends on the
|
|||
and
|
||||
.Sy local
|
||||
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
|
||||
.Pp
|
||||
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
|
||||
chmod +x ~/.session
|
||||
.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
|
||||
.Xr trianglix 1
|
||||
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
|
||||
submenu and then selecting which ports.
|
||||
.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
|
||||
selecting
|
||||
.Sy Disable network drivers .
|
||||
|
@ -180,6 +189,17 @@ If not, you can run the installer by running the
|
|||
.Xr sysinstall 8
|
||||
command.
|
||||
.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
|
||||
you answer them.
|
||||
It provides useful information you shouldn't accidentally overlook.
|
||||
|
@ -386,6 +406,14 @@ and
|
|||
.Pp
|
||||
Please note that Sortix is not currently secure as a multi-user system and
|
||||
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
|
||||
You will be asked if you want to enable the Network Time Protocol client
|
||||
.Xr ntpd 8 ,
|
||||
|
@ -519,6 +547,7 @@ fragment instead.
|
|||
.Sh SEE ALSO
|
||||
.Xr chkblayout 1 ,
|
||||
.Xr chvideomode 1 ,
|
||||
.Xr display 1 ,
|
||||
.Xr man 1 ,
|
||||
.Xr fstab 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
|
||||
.Fl N
|
||||
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
|
||||
To customize a release so it automatically installs itself per the
|
||||
.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,
|
||||
installing the operating system, or upgrading an existing installation.
|
||||
.Pp
|
||||
You will be presented a with standard Unix command line environment upon login or
|
||||
booting the live environment.
|
||||
.Ss Shutdown
|
||||
.Xr init 8
|
||||
spawns a session after boot.
|
||||
This is
|
||||
.Xr login 8
|
||||
if the system is booted in multi-user mode.
|
||||
This is a root shell if booted in single-user mode.
|
||||
You will be presented with a graphical Unix-like command line environment upon
|
||||
login or booting the live environment.
|
||||
.Ss Desktop Environment
|
||||
The
|
||||
.Xr display 1
|
||||
desktop environment is automatically started when booting the live environment
|
||||
or after logging into an installation.
|
||||
.Pp
|
||||
To power off the computer login as user
|
||||
.Sy poweroff
|
||||
or run
|
||||
A new
|
||||
.Xr terminal 1
|
||||
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
|
||||
after logging in.
|
||||
To reboot the computer login as user
|
||||
.Sy reboot
|
||||
or run
|
||||
or login as
|
||||
.Sy poweroff .
|
||||
To reboot the computter, run
|
||||
.Xr reboot 8
|
||||
after logging in.
|
||||
or login as
|
||||
.Sy reboot .
|
||||
.Ss Keyboard Layout
|
||||
The kernel has a default US keyboard layout compiled into it.
|
||||
.Pp
|
||||
|
|
|
@ -60,13 +60,13 @@ install: all
|
|||
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-group
|
||||
|
||||
sysinstall: $(SYSINSTALL_OBJS)
|
||||
$(CC) $(SYSINSTALL_OBJS) -o $@ -lmount
|
||||
$(CC) $(SYSINSTALL_OBJS) -o $@ -lmount -ldisplay
|
||||
|
||||
sysmerge: $(SYSMERGE_OBJS)
|
||||
$(CC) $(SYSMERGE_OBJS) -o $@ -lmount
|
||||
|
||||
sysupgrade: $(SYSUPGRADE_OBJS)
|
||||
$(CC) $(SYSUPGRADE_OBJS) -o $@ -lmount
|
||||
$(CC) $(SYSUPGRADE_OBJS) -o $@ -lmount -ldisplay
|
||||
|
||||
%.o: %.c
|
||||
$(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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -17,11 +18,15 @@
|
|||
* Interactive utility functions.
|
||||
*/
|
||||
|
||||
#include <sys/display.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/termmode.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <display.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
@ -31,10 +36,22 @@
|
|||
#include <termios.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include <display.h>
|
||||
|
||||
#include "autoconf.h"
|
||||
#include "execute.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)
|
||||
{
|
||||
long shlvl = 0;
|
||||
|
@ -259,3 +276,107 @@ bool missing_program(const char* program)
|
|||
warnx("%s: Program is absent", program);
|
||||
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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -42,5 +42,7 @@ void password(char* buffer,
|
|||
size_t buffer_size,
|
||||
const char* question);
|
||||
bool missing_program(const char* program);
|
||||
bool get_video_mode(struct dispmsg_crtc_mode* mode);
|
||||
void gui_shutdown(int code);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -348,6 +348,7 @@ static bool etc_made = false;
|
|||
static char etc[] = "/tmp/etc.XXXXXX";
|
||||
static bool fs_made = false;
|
||||
static char fs[] = "/tmp/fs.XXXXXX";
|
||||
static int exit_gui_code = -1;
|
||||
|
||||
static void unmount_all_but_root(void)
|
||||
{
|
||||
|
@ -376,6 +377,14 @@ void exit_handler(void)
|
|||
rmdir(fs);
|
||||
if ( etc_made )
|
||||
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)
|
||||
|
@ -544,7 +553,8 @@ int main(void)
|
|||
|
||||
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 )
|
||||
{
|
||||
// TODO: Detect the name of the current keyboard layout.
|
||||
|
@ -598,38 +608,16 @@ int main(void)
|
|||
text("\n");
|
||||
}
|
||||
|
||||
struct tiocgdisplay display;
|
||||
struct tiocgdisplays gdisplays;
|
||||
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_crtc_mode mode;
|
||||
if ( get_video_mode(&mode) )
|
||||
{
|
||||
struct dispmsg_get_crtc_mode get_mode;
|
||||
memset(&get_mode, 0, sizeof(get_mode));
|
||||
get_mode.msgid = DISPMSG_GET_CRTC_MODE;
|
||||
get_mode.device = 0;
|
||||
get_mode.connector = 0;
|
||||
bool good = false;
|
||||
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) == 0 )
|
||||
bool good = (mode.control & DISPMSG_CONTROL_VALID) &&
|
||||
(mode.control & DISPMSG_CONTROL_GOOD_DEFAULT);
|
||||
if ( mode.control & DISPMSG_CONTROL_VM_AUTO_SCALE )
|
||||
{
|
||||
good = (get_mode.mode.control & DISPMSG_CONTROL_VALID) &&
|
||||
(get_mode.mode.control & DISPMSG_CONTROL_GOOD_DEFAULT);
|
||||
if ( get_mode.mode.control & DISPMSG_CONTROL_VM_AUTO_SCALE )
|
||||
{
|
||||
text("The display resolution will automatically change to "
|
||||
"match the size of the virtual machine window.\n\n");
|
||||
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";
|
||||
while ( true )
|
||||
|
@ -648,14 +636,12 @@ int main(void)
|
|||
if ( execute((const char*[]) { "chvideomode", r, NULL }, "f") != 0 )
|
||||
continue;
|
||||
input[0] = '\0';
|
||||
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) < 0 ||
|
||||
!(get_mode.mode.control & DISPMSG_CONTROL_VALID) ||
|
||||
get_mode.mode.control & DISPMSG_CONTROL_VGA )
|
||||
break;
|
||||
if ( !get_video_mode(&mode) ||
|
||||
!(mode.control & DISPMSG_CONTROL_VALID) ||
|
||||
mode.control & DISPMSG_CONTROL_VGA )
|
||||
continue;
|
||||
snprintf(input, sizeof(input), "%ux%ux%u",
|
||||
get_mode.mode.view_xres,
|
||||
get_mode.mode.view_yres,
|
||||
get_mode.mode.fb_format);
|
||||
mode.view_xres, mode.view_yres, mode.fb_format);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -918,7 +904,7 @@ int main(void)
|
|||
{
|
||||
prompt(input, sizeof(input), "confirm_install",
|
||||
"Install " BRAND_DISTRIBUTION_NAME "? "
|
||||
"(yes/no/poweroff/reboot/halt)", "yes");
|
||||
"(yes/no/exit/poweroff/reboot/halt)", "yes");
|
||||
if ( !strcasecmp(input, "yes") )
|
||||
break;
|
||||
else if ( !strcasecmp(input, "no") )
|
||||
|
@ -929,12 +915,14 @@ int main(void)
|
|||
"'halt' to cancel the installation.\n");
|
||||
continue;
|
||||
}
|
||||
else if ( !strcasecmp(input, "poweroff") )
|
||||
else if ( !strcasecmp(input, "exit") )
|
||||
exit(0);
|
||||
else if ( !strcasecmp(input, "poweroff") )
|
||||
exit_gui(0);
|
||||
else if ( !strcasecmp(input, "reboot") )
|
||||
exit(1);
|
||||
exit_gui(1);
|
||||
else if ( !strcasecmp(input, "halt") )
|
||||
exit(2);
|
||||
exit_gui(2);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
@ -1293,6 +1281,27 @@ int main(void)
|
|||
|
||||
// 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) )
|
||||
{
|
||||
text("A Network Time Protocol client (ntpd) has been installed that "
|
||||
|
@ -1538,11 +1547,11 @@ int main(void)
|
|||
if ( !strcasecmp(input, "exit") )
|
||||
exit(0);
|
||||
else if ( !strcasecmp(input, "poweroff") )
|
||||
exit(0);
|
||||
exit_gui(0);
|
||||
else if ( !strcasecmp(input, "reboot") )
|
||||
exit(1);
|
||||
exit_gui(1);
|
||||
else if ( !strcasecmp(input, "halt") )
|
||||
exit(2);
|
||||
exit_gui(2);
|
||||
else if ( !strcasecmp(input, "boot") )
|
||||
{
|
||||
if ( !access("/etc/fstab", F_OK) )
|
||||
|
@ -1557,7 +1566,7 @@ int main(void)
|
|||
"echo 'require chain exit-code' > "
|
||||
"/etc/init/default", NULL },
|
||||
"ef");
|
||||
exit(3);
|
||||
exit_gui(3);
|
||||
}
|
||||
else if ( !strcasecmp(input, "chroot") )
|
||||
{
|
||||
|
|
|
@ -73,6 +73,7 @@ static struct mountpoint* mountpoints;
|
|||
static size_t mountpoints_used;
|
||||
static bool fs_made = false;
|
||||
static char fs[] = "/tmp/fs.XXXXXX";
|
||||
static int exit_gui_code = -1;
|
||||
|
||||
static bool add_installation(struct blockdevice* bdev,
|
||||
struct release* release,
|
||||
|
@ -329,6 +330,14 @@ void exit_handler(void)
|
|||
}
|
||||
if ( fs_made )
|
||||
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)
|
||||
|
@ -457,7 +466,8 @@ int main(void)
|
|||
prompt(input, sizeof(input), "ready", "Ready?", ready);
|
||||
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 )
|
||||
{
|
||||
// TODO: Detect the name of the current keyboard layout.
|
||||
|
@ -499,38 +509,16 @@ int main(void)
|
|||
if ( kblayout_setable )
|
||||
text("\n");
|
||||
|
||||
struct tiocgdisplay display;
|
||||
struct tiocgdisplays gdisplays;
|
||||
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_crtc_mode mode;
|
||||
if ( get_video_mode(&mode) )
|
||||
{
|
||||
struct dispmsg_get_crtc_mode get_mode;
|
||||
memset(&get_mode, 0, sizeof(get_mode));
|
||||
get_mode.msgid = DISPMSG_GET_CRTC_MODE;
|
||||
get_mode.device = 0;
|
||||
get_mode.connector = 0;
|
||||
bool good = false;
|
||||
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) == 0 )
|
||||
bool good = (mode.control & DISPMSG_CONTROL_VALID) &&
|
||||
(mode.control & DISPMSG_CONTROL_GOOD_DEFAULT);
|
||||
if ( mode.control & DISPMSG_CONTROL_VM_AUTO_SCALE )
|
||||
{
|
||||
good = (get_mode.mode.control & DISPMSG_CONTROL_VALID) &&
|
||||
(get_mode.mode.control & DISPMSG_CONTROL_GOOD_DEFAULT);
|
||||
if ( get_mode.mode.control & DISPMSG_CONTROL_VM_AUTO_SCALE )
|
||||
{
|
||||
text("The display resolution will automatically change to "
|
||||
"match the size of the virtual machine window.\n\n");
|
||||
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";
|
||||
while ( true )
|
||||
|
@ -835,7 +823,7 @@ int main(void)
|
|||
while ( true )
|
||||
{
|
||||
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") )
|
||||
break;
|
||||
else if ( !strcasecmp(input, "no") )
|
||||
|
@ -848,12 +836,14 @@ int main(void)
|
|||
"'halt' or cancel the upgrade.\n");
|
||||
continue;
|
||||
}
|
||||
else if ( !strcasecmp(input, "poweroff") )
|
||||
else if ( !strcasecmp(input, "exit") )
|
||||
exit(0);
|
||||
else if ( !strcasecmp(input, "poweroff") )
|
||||
exit_gui(0);
|
||||
else if ( !strcasecmp(input, "reboot") )
|
||||
exit(1);
|
||||
exit_gui(1);
|
||||
else if ( !strcasecmp(input, "halt") )
|
||||
exit(2);
|
||||
exit_gui(2);
|
||||
else if ( !strcasecmp(input, "!") )
|
||||
break;
|
||||
else
|
||||
|
@ -991,12 +981,14 @@ int main(void)
|
|||
while ( true )
|
||||
{
|
||||
prompt(input, sizeof(input), "finally",
|
||||
"What now? (poweroff/reboot/halt)", "reboot");
|
||||
if ( !strcasecmp(input, "poweroff") )
|
||||
return 0;
|
||||
if ( !strcasecmp(input, "reboot") )
|
||||
return 1;
|
||||
if ( !strcasecmp(input, "halt") )
|
||||
return 2;
|
||||
"What now? (exit/poweroff/reboot/halt)", "reboot");
|
||||
if ( !strcasecmp(input, "exit") )
|
||||
exit(0);
|
||||
else if ( !strcasecmp(input, "poweroff") )
|
||||
exit_gui(0);
|
||||
else if ( !strcasecmp(input, "reboot") )
|
||||
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=
|
||||
enable_append_title=true
|
||||
enable_dhclient=
|
||||
enable_gui=
|
||||
enable_network_drivers=
|
||||
enable_ntpd=
|
||||
enable_src=
|
||||
|
@ -56,12 +57,14 @@ for argument do
|
|||
--default) previous_option=default ;;
|
||||
--disable-append-title) enable_append_title=false ;;
|
||||
--disable-dhclient) enable_dhclient=false ;;
|
||||
--disable-gui) enable_gui=false ;;
|
||||
--disable-network-drivers) enable_network_drivers=false ;;
|
||||
--disable-ntpd) enable_ntpd=false ;;
|
||||
--disable-src) enable_src=false ;;
|
||||
--disable-sshd) enable_sshd=false ;;
|
||||
--enable-append-title) enable_append_title=true ;;
|
||||
--enable-dhclient) enable_dhclient=true ;;
|
||||
--enable-gui) enable_gui=true ;;
|
||||
--enable-network-drivers) enable_network_drivers=true ;;
|
||||
--enable-ntpd) enable_ntpd=true ;;
|
||||
--enable-src) enable_src=true ;;
|
||||
|
@ -162,6 +165,7 @@ mkdir -p -- "$directory/boot/grub"
|
|||
echo "title_sysupgrade='***AUTOMATIC UPGRADE***'"
|
||||
fi
|
||||
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_bool "$enable_src" src src
|
||||
print_enable_default_bool "$enable_sshd" sshd sshd
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
.Op Fl \-default Ns = Ns Ar default-boot-menu-option
|
||||
.Op Fl \-disable-append-title
|
||||
.Op Fl \-disable-dhclient
|
||||
.Op Fl \-disable-gui
|
||||
.Op Fl \-disable-network-drivers
|
||||
.Op Fl \-disable-ntpd
|
||||
.Op Fl \-disable-src
|
||||
.Op Fl \-disable-sshd
|
||||
.Op Fl \-enable-append-title
|
||||
.Op Fl \-enable-dhclient
|
||||
.Op Fl \-enable-gui
|
||||
.Op Fl \-enable-network-drivers
|
||||
.Op Fl \-enable-ntpd
|
||||
.Op Fl \-enable-src
|
||||
|
@ -105,6 +107,16 @@ GRUB variable to
|
|||
causing the bootloader to load additional configuration that turns off the
|
||||
.Xr dhclient 8
|
||||
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
|
||||
Disable network drivers by setting the
|
||||
.Sy enable_network_drivers
|
||||
|
@ -152,6 +164,16 @@ GRUB variable to
|
|||
selecting the default behavior of starting the
|
||||
.Xr dhclient 8
|
||||
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
|
||||
Enable network drivers by setting the
|
||||
.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-add sortix.iso bootconfig
|
||||
.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
|
||||
.Xr xorriso 1 ,
|
||||
.Xr kernel 7 ,
|
||||
|
|
|
@ -11,11 +11,9 @@ CXXFLAGS:=$(CXXFLAGS) -std=gnu++11 -Wall -Wextra -fno-exceptions -fno-rtti -fche
|
|||
|
||||
BINARY:=trianglix
|
||||
|
||||
LIBS:=-ldispd
|
||||
|
||||
all: $(BINARY)
|
||||
|
||||
.PHONY: all install uninstall clean
|
||||
.PHONY: all install clean
|
||||
|
||||
install: all
|
||||
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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -26,7 +26,7 @@
|
|||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <dispd.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -892,13 +892,6 @@ public:
|
|||
buffer(buffer), pitch(pitch), xres(xres), yres(yres) { }
|
||||
FrameBufferInfo(const FrameBufferInfo& o) :
|
||||
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() { }
|
||||
|
||||
public:
|
||||
|
@ -1414,14 +1407,45 @@ static void Render(FrameBufferInfo fb, struct Desktop* 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);
|
||||
if ( !fb )
|
||||
return false;
|
||||
FrameBufferInfo fbinfo(fb);
|
||||
struct dispmsg_get_crtc_mode get_mode_msg;
|
||||
memset(&get_mode_msg, 0, sizeof(get_mode_msg));
|
||||
get_mode_msg.msgid = DISPMSG_GET_CRTC_MODE;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1815,7 +1839,7 @@ static void HandleEvents(int kbfd, struct Desktop* desktop)
|
|||
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) 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++ )
|
||||
{
|
||||
HandleEvents(kbfd, &info.desktop);
|
||||
if ( !Render(window, &info) )
|
||||
if ( !Render(&info) )
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -1873,27 +1897,13 @@ int main(int argc, char* argv[])
|
|||
|
||||
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();
|
||||
if ( kbfd < 0 )
|
||||
error(1, 0, "couldn't create keyboard connection");
|
||||
|
||||
int ret = MainLoop(argc, argv, kbfd, window);
|
||||
int ret = MainLoop(argc, argv, kbfd);
|
||||
|
||||
close(kbfd);
|
||||
|
||||
dispd_destroy_window(window);
|
||||
dispd_detach_session(session);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,10 @@
|
|||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue