Add display server.

Remove the obsolete dispd.
This commit is contained in:
Jonas 'Sortie' Termansen 2014-04-07 03:11:29 +02:00
parent d19eb24b27
commit f14dcc6953
65 changed files with 6438 additions and 710 deletions

View File

@ -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 \
@ -34,6 +36,7 @@ rw \
sf \
sh \
sysinstall \
terminal \
tix \
trianglix \
update-initrd \

View File

@ -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

View File

@ -151,6 +151,7 @@ EOF
tix-iso-bootconfig \
--random-seed \
--timeout=0 \
--disable-gui \
--liveconfig=liveconfig \
bootconfig
mkdir -p bootconfig/boot/grub

View File

@ -11,7 +11,7 @@ CFLAGS += -Wall -Wextra
BINARIES = chkblayout
MANPAGES1 = chkblayout.1
LIBS =
LIBS = -ldisplay
all: $(BINARIES)

View File

@ -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, int 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);

3
dispd/.gitignore vendored
View File

@ -1,3 +0,0 @@
*.a
*.o
server/dispd

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

3
display/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
display
*.o
*.inc

51
display/Makefile Normal file
View File

@ -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

BIN
display/arrow.rgb Normal file

Binary file not shown.

350
display/connection.c Normal file
View File

@ -0,0 +1,350 @@
/*
* 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/socket.h>
#include <sys/termmode.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 <termios.h>
#include <unistd.h>
#include "connection.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 )
{
size_t required_size = connection->outgoing_used + count;
unsigned char* new_outgoing = (unsigned char*) malloc(required_size);
size_t first_part_available = connection->outgoing_size - connection->outgoing_offset;
size_t first_part = connection->outgoing_used < first_part_available ?
connection->outgoing_used :
first_part_available;
if ( connection->outgoing )
{
memcpy(new_outgoing, connection->outgoing +
connection->outgoing_offset, first_part);
size_t second_part = connection->outgoing_used - first_part;
memcpy(new_outgoing + first_part, connection->outgoing, second_part);
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_part_available = connection->outgoing_size - used_offset;
size_t first_part = count < first_part_available ? count : first_part_available;
memcpy(connection->outgoing + used_offset, buffer, first_part);
size_t second_part = count - first_part;
memcpy(connection->outgoing, (const unsigned char*) buffer + first_part, second_part);
connection->outgoing_used += count;
}
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_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);
}
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;
window->show = true;
}
CONNECTION_MESSAGE_HANDLER_NO_AUX(hide_window)
{
struct window* window = connection_find_window(connection, msg->window_id);
if ( !window )
return;
window->show = false;
}
CONNECTION_MESSAGE_HANDLER_SERVER(chkblayout)
{
struct event_ack event;
event.id = msg->id;
event.error = 0;
if ( tcsetblob(server->tty_fd, "kblayout", auxiliary, auxiliary_size) < 0 )
event.error = errno;
struct display_packet_header header;
header.message_id = EVENT_ACK;
header.message_length = sizeof(event);
connection_schedule_transmit(connection, &header, sizeof(header));
connection_schedule_transmit(connection, &event, sizeof(event));
}
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),
};
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 and disconnect.
connection->packet_header_received += amount;
}
size_t packet_length = connection->packet_header.message_length;
if ( !connection->packet )
connection->packet = (unsigned char*) malloc(packet_length);
while ( connection->packet_received < packet_length )
{
ssize_t amount = read(connection->fd,
connection->packet + connection->packet_received,
packet_length - connection->packet_received);
if ( amount < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) )
return;
if ( amount < 0 || amount == 0 )
return; // TODO: No longer signal interest in reading and disconnect.
connection->packet_received += amount;
}
size_t packet_id = connection->packet_header.message_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_length - 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 received 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;
ssize_t amount = write(connection->fd, connection->outgoing + connection->outgoing_offset, count);
if ( amount < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) )
return;
if ( amount < 0 || amount == 0 )
return; // TODO: No longer signal interest in writing and 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);
}

67
display/connection.h Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

745
display/display-code.c Normal file
View File

@ -0,0 +1,745 @@
/*
* Copyright (c) 2014, 2015, 2016, 2018, 2022 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 <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));
}
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);
}
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);
}
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;
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);
}
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;
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);
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_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_render(struct display* display)
{
struct dispmsg_crtc_mode mode;
{
struct dispmsg_get_crtc_mode msg;
memset(&msg, 0, sizeof(msg));
msg.msgid = DISPMSG_GET_CRTC_MODE;
msg.device = 0; // TODO: Multi-screen support!
msg.connector = 0; // TODO: Multi-screen support!
if ( dispmsg_issue(&msg, sizeof(msg)) != 0 )
err(1, "dispmsg_issue: dispmsg_get_crtc_mode");
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");
struct framebuffer fb;
fb.xres = mode.view_xres;
fb.yres = mode.view_yres;
fb.pitch = mode.view_xres;
size_t framebuffer_length = fb.xres * fb.yres;
fb.buffer = (uint32_t*) malloc(sizeof(uint32_t) * framebuffer_length);
size_t framebuffer_size = framebuffer_length * sizeof(fb.buffer[0]);
display_on_resolution_change(display, mode.view_xres, mode.view_yres);
display_composit(display, fb);
{
struct dispmsg_write_memory msg;
memset(&msg, 0, sizeof(msg));
msg.msgid = DISPMSG_WRITE_MEMORY;
msg.device = 0; // TODO: Multi-screen support!
msg.offset = 0; // TODO: mode.fb_location!
msg.size = framebuffer_size;
msg.src = (uint8_t*) fb.buffer;
if ( dispmsg_issue(&msg, sizeof(msg)) != 0 )
err(1, "dispmsg_issue: dispmsg_write_memory");
}
// TODO: This could be recycled if the resolution did not change.
free(fb.buffer);
}
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.message_id = EVENT_KEYBOARD;
header.message_length = 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;
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 ( bytes[0] & MOUSE_BUTTON_LEFT )
{
display_set_active_window(display, window);
if ( display->mouse_state == MOUSE_STATE_NONE )
{
// TODO: Stay in state until mouse release.
if ( display->key_lalt ||
(0 <= window_pointer_x &&
window_pointer_x < (ssize_t) window->width &&
0 <= window_pointer_y &&
window_pointer_y <= (ssize_t) TITLE_HEIGHT) )
{
size_t border_width = window_border_width(window);
size_t button_width = FONT_WIDTH * 2;
ssize_t buttons_x = window->width - border_width
- button_width * 3 + 1;
ssize_t rel_x = window_pointer_x - buttons_x;
if ( 0 <= rel_x && rel_x < (ssize_t) button_width )
{
// TODO Minimize window.
}
else if ( 0 <= rel_x && rel_x < (ssize_t) button_width * 2 )
window_toggle_maximized(window);
else if ( 0 <= rel_x && rel_x < (ssize_t) button_width * 3 )
window_quit(window);
else
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 ( 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_TITLE_MOVE:
if ( clipped_edge )
{
// I'd declare those in function scope but I'm afraid of
// messing with the code too much.
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
{
display->mouse_state = MOUSE_STATE_NONE;
}
}
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);
}

161
display/display.1 Normal file
View File

@ -0,0 +1,161 @@
.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 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
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 /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 /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

143
display/display.c Normal file
View File

@ -0,0 +1,143 @@
/*
* Copyright (c) 2014, 2015, 2016, 2017, 2022 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");
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
{
while ( i < *argc && !(*argv)[i] )
{
for ( int n = i; n < *argc; n++ )
(*argv)[n] = (*argv)[n+1];
(*argc)--;
}
}
}
int main(int argc, char* argv[])
{
for ( int i = 1; i < argc; i++ )
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
{
char c;
while ( (c = *++arg) ) switch ( c )
{
default:
errx(1, "unknown option -- '%c'", c);
}
}
else
errx(1, "unknown option: %s", arg);
}
compact_arguments(&argc, &argv);
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);
if ( setenv("DISPLAY_SOCKET", server.server_path, 1) < 0 )
err(1, "setenv");
ready();
char* home_session = NULL;
char** session_argv = NULL;
if ( 1 < argc )
session_argv = argv + 1;
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;
}

88
display/display.h Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2014, 2015, 2016, 2022 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/ps2mouse.h>
#include <stdbool.h>
#include <stddef.h>
#include "damage-rect.h"
#include "framebuffer.h"
enum mouse_state
{
MOUSE_STATE_NONE,
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 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;
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_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

77
display/displayrc.5 Normal file
View File

@ -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

272
display/server.c Normal file
View File

@ -0,0 +1,272 @@
/*
* 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.
*
* server.c
* Display server main loop.
*/
#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 = (struct sockaddr_un*) 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)
{
memset(server, 0, sizeof(*server));
server->display = display;
load_font();
server->tty_fd = 0;
if ( !isatty(server->tty_fd) )
{
server->tty_fd = open("/dev/tty", O_RDONLY);
if ( server->tty_fd < 0 )
err(1, "/dev/tty");
}
server->mouse_fd = open("/dev/mouse", O_RDONLY | O_CLOEXEC);
if ( server->mouse_fd < 0 )
err(1, "%s", "/dev/mouse");
server->server_path = "/run/display";
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(0, termmode) < 0 )
err(1, "settermmode");
server->pfds_count = server_pfds_count(server);
server->pfds = (struct pollfd*)
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 = (struct connection**)
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 = (struct pollfd*)
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 = (struct 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 )
{
// TODO: Only do this if a redraw is actually needed.
display_render(server->display);
server_poll(server);
}
}

50
display/server.h Normal file
View File

@ -0,0 +1,50 @@
/*
* 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 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);
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

543
display/window.c Normal file
View File

@ -0,0 +1,543 @@
/*
* Copyright (c) 2014, 2015, 2016, 2017, 2022 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 <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_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);
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 = window_border_width(window);
size_t button_width = FONT_WIDTH * 2;
size_t button_height = FONT_HEIGHT;
ssize_t buttons_x = window->width - border_width - button_width * 3 + 1;
struct framebuffer buttons_fb =
framebuffer_crop(window->buffer, buttons_x,
tt_pos_y, button_width * 3, tt_height);
size_t button_size = FONT_WIDTH - 1;
#if 0
for (size_t x = 0; x < button_width; x++)
for (size_t y = 0; y < button_height; y++)
framebuffer_set_pixel(buttons_fb, button_width * 0 + x, y, 0xFF8080FF);
for (size_t x = 0; x < button_width; x++)
for (size_t y = 0; y < button_height; y++)
framebuffer_set_pixel(buttons_fb, button_width * 1 + x, y, 0xFFFF8080);
for (size_t x = 0; x < button_width; x++)
for (size_t y = 0; y < button_height; y++)
framebuffer_set_pixel(buttons_fb, button_width * 2 + x, y, 0xFF8080FF);
#endif
for ( size_t i = 0; i < button_size; i++ )
{
size_t bx = button_width * 0 + (button_width - button_size) / 2;
size_t by = (button_height - button_size) / 2;
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_width * 1 + (button_width - button_size) / 2;
size_t by = (button_height - button_size) / 2;
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_width * 2 + (button_width - button_size) / 2;
size_t by = (button_height - button_size) / 2;
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_width * 3 * q / q_width);
ssize_t tt_pos_x = (center_over - tt_width) / 2;
#if 0
for (int y = 0; y < 10; y++)
{
framebuffer_set_pixel(window->buffer, window->width / 2, y, 0xFFFF0000);
framebuffer_set_pixel(window->buffer, (window->width - button_width * 3) / 2, y, 0xFFFF0000);
framebuffer_set_pixel(window->buffer, center_over/2, y, 0xFFFF00FF);
}
#endif
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);
}
void window_move(struct window* window, size_t left, size_t top)
{
window->left = left;
window->top = top;
}
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;
window->buffer.buffer = (uint32_t*)
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);
}
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 = NULL;
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_keyboard event;
event.window_id = window->window_id;
struct display_packet_header header;
header.message_id = EVENT_QUIT;
header.message_length = 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: Potentially 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;
window->buffer.xres = window->width;
window->buffer.yres = window->height;
window->buffer.pitch = window->width;
window->buffer.buffer = (uint32_t*)
malloc(sizeof(uint32_t) * window->width * window->height);
memset(window->buffer.buffer, 0, 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.message_id = EVENT_RESIZE;
header.message_length = sizeof(event);
assert(window->connection);
connection_schedule_transmit(window->connection, &header, sizeof(header));
connection_schedule_transmit(window->connection, &event, sizeof(event));
}
size_t window_border_width(const struct window* window)
{
bool maximized = window->window_state != WINDOW_STATE_REGULAR;
return maximized ? BORDER_WIDTH / 2 : BORDER_WIDTH;
}

107
display/window.h Normal file
View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2014, 2015, 2016, 2018, 2022 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 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;
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;
bool created;
bool show;
bool focus;
bool grab_input;
};
struct framebuffer window_client_buffer(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);
size_t window_border_width(const struct window* window);
#endif

View File

@ -15,7 +15,7 @@ BINARIES:=\
asteroids \
aquatinspitz \
LIBS:=-ldispd
LIBS:=-ldisplay
all: $(BINARIES)

View File

@ -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, &current_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;
}

View File

@ -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;
}

2
libdisplay/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.a
*.o

34
libdisplay/Makefile Normal file
View File

@ -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

View File

@ -0,0 +1,128 @@
/*
* Copyright (c) 2014, 2015, 2016 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 <stdint.h>
struct display_packet_header
{
uint32_t message_id;
uint32_t message_length;
};
#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;
/* An 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 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;
int error;
};
#endif

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2014, 2015, 2016, 2017 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 <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);
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, int);
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;
};
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

362
libdisplay/libdisplay.c Normal file
View File

@ -0,0 +1,362 @@
/*
* Copyright (c) 2014, 2015, 2016, 2017 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)
{
// TODO: Overflow.
char** new_argv = malloc((2 + argc + 1) * 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 = (struct sockaddr_un*) 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_bytes;
uint8_t* payload;
size_t payload_bytes;
};
struct display_connection* display_connect(const char* socket_path)
{
struct display_connection* connection =
(struct display_connection*) malloc(sizeof(struct display_connection));
if ( !connection )
return NULL;
memset(connection, 0, sizeof(*connection));
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") :
"/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 message_id,
const void* message,
size_t message_size,
const void* auxiliary,
size_t auxiliary_size)
{
struct display_packet_header header;
header.message_id = message_id;
header.message_length = 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 message_id,
const void* message,
size_t message_size)
{
send_message(connection, message_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);
}
static bool display_read_event(struct display_connection* connection)
{
while ( connection->header_bytes < sizeof(connection->header) )
{
errno = 0;
ssize_t amount = read(connection->fd,
(uint8_t*) &connection->header + connection->header_bytes,
sizeof(connection->header) - connection->header_bytes);
if ( amount < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) )
break;
if ( amount < 0 )
break;
if ( amount == 0 )
return false;
connection->header_bytes += amount;
}
if ( connection->header_bytes == sizeof(connection->header) &&
!connection->payload )
{
connection->payload = (uint8_t*) malloc(connection->header.message_length);
connection->payload_bytes = 0;
}
while ( connection->header_bytes == sizeof(connection->header) &&
connection->payload &&
connection->payload_bytes < connection->header.message_length )
{
errno = 0;
ssize_t amount = read(connection->fd,
connection->payload + connection->payload_bytes,
connection->header.message_length - connection->payload_bytes);
if ( amount < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) )
break;
if ( amount < 0 )
break;
if ( amount == 0 )
return false;
connection->payload_bytes += amount;
}
return true;
}
static int display_dispatch_event(struct display_connection* connection,
struct display_event_handlers* handlers)
{
if ( connection->header_bytes == sizeof(connection->header) &&
connection->payload &&
connection->payload_bytes == connection->header.message_length )
{
// TODO: == instead of <= due to warning. Maybe all should be changed to
// use == instead.
if ( connection->header.message_id == EVENT_DISCONNECT &&
connection->header.message_length ==/*>=*/ sizeof(struct event_disconnect) )
{
struct event_disconnect* event = (struct event_disconnect*) connection->payload;
(void) event;
if ( handlers->disconnect_handler )
handlers->disconnect_handler(handlers->context);
else
exit(0);
}
if ( connection->header.message_id == EVENT_QUIT &&
connection->header.message_length >= sizeof(struct event_quit) )
{
struct event_quit* event = (struct event_quit*) connection->payload;
if ( handlers->quit_handler )
handlers->quit_handler(handlers->context, event->window_id);
else
exit(0);
}
if ( connection->header.message_id == EVENT_RESIZE &&
connection->header.message_length >= sizeof(struct event_resize) )
{
struct event_resize* event = (struct event_resize*) connection->payload;
if ( handlers->resize_handler )
handlers->resize_handler(handlers->context, event->window_id, event->width, event->height);
}
if ( connection->header.message_id == EVENT_KEYBOARD &&
connection->header.message_length >= sizeof(struct event_keyboard) )
{
struct event_keyboard* event = (struct event_keyboard*) connection->payload;
if ( handlers->keyboard_handler )
handlers->keyboard_handler(handlers->context, event->window_id, event->codepoint);
}
if ( connection->header.message_id == EVENT_ACK &&
connection->header.message_length >= sizeof(struct event_ack) )
{
struct event_ack* event = (struct event_ack*) connection->payload;
if ( handlers->ack_handler )
handlers->ack_handler(handlers->context, event->id, event->error);
}
connection->header_bytes = 0;
free(connection->payload), connection->payload = NULL;
connection->payload_bytes = 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);
}

2
libui/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.a
*.o

36
libui/Makefile Normal file
View File

@ -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

71
libui/framebuffer.c Normal file
View File

@ -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));
}
}
}

View File

@ -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

55
libui/include/pixel.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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;
}

343
libui/vgafont.c Normal file
View File

@ -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);
}

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ?

View File

@ -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:

View File

@ -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

View File

@ -386,6 +386,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 +527,7 @@ fragment instead.
.Sh SEE ALSO
.Xr chkblayout 1 ,
.Xr chvideomode 1 ,
.Xr display 1 ,
.Xr man 1 ,
.Xr fstab 5 ,
.Xr group 5 ,

View File

@ -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 :

View File

@ -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
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
A new
.Xr terminal 1
can be launched by pressing Control + Alt + F1.
.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

View File

@ -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 $@

View File

@ -31,6 +31,8 @@
#include <termios.h>
#include <wchar.h>
#include <display.h>
#include "autoconf.h"
#include "execute.h"
#include "interactive.h"
@ -259,3 +261,15 @@ bool missing_program(const char* program)
warnx("%s: Program is absent", program);
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");
}
}

View File

@ -42,5 +42,6 @@ void password(char* buffer,
size_t buffer_size,
const char* question);
bool missing_program(const char* program);
void gui_shutdown(int code);
#endif

View File

@ -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.
@ -918,7 +928,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 +939,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 +1305,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 +1571,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 +1590,7 @@ int main(void)
"echo 'require chain exit-code' > "
"/etc/init/default", NULL },
"ef");
exit(3);
exit_gui(3);
}
else if ( !strcasecmp(input, "chroot") )
{

View File

@ -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.
@ -835,7 +845,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 +858,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 +1003,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);
}
}

2
terminal/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
terminal
*.o

36
terminal/Makefile Normal file
View File

@ -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

283
terminal/palette.h Normal file
View File

@ -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

44
terminal/terminal.1 Normal file
View File

@ -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

1292
terminal/terminal.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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 ,

View File

@ -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