Add display server.

This commit is contained in:
Jonas 'Sortie' Termansen 2014-04-07 03:11:29 +02:00
parent 7f09c8ff95
commit b98d0d1f18
60 changed files with 6847 additions and 123 deletions

View File

@ -7,12 +7,15 @@ MODULES=\
libc \
libm \
dispd \
libdisplay \
libmount \
libui \
bench \
carray \
checksum \
dhclient \
disked \
display \
dnsconfig \
editor \
ext \
@ -26,12 +29,14 @@ kblayout \
kblayout-compiler \
login \
mkinitrd \
nyan \
ping \
regress \
rw \
sf \
sh \
sysinstall \
terminal \
tix \
trianglix \
update-initrd \

View File

@ -195,6 +195,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
@ -208,6 +209,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
@ -396,10 +398,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
@ -412,7 +415,7 @@ menu_title="\$base_menu_title"
hook_menu_pre
EOF
menuentry "live environment" '-- /sbin/init'
menuentry "live environment" '-- /sbin/init --target=single-user'
menuentry "new installation" '-- /sbin/init --target=sysinstall'
menuentry "upgrade existing installation" '-- /sbin/init --target=sysupgrade'
@ -440,6 +443,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

3
display/.gitignore vendored Normal file
View File

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

45
display/Makefile Normal file
View File

@ -0,0 +1,45 @@
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
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
$(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.

318
display/connection.c Normal file
View File

@ -0,0 +1,318 @@
/*
* 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.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 <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* auxilerary __attribute__((unused)), \
size_t auxilerary_size __attribute__((unused)))
#define CONNECTION_MESSAGE_HANDLER(message_name) \
void connection_handler_##message_name(struct connection* connection, \
struct display_##message_name* msg, \
unsigned char* auxilerary, \
size_t auxilerary_size)
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
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*) auxilerary;
if ( auxilerary_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*) auxilerary;
free(window->title);
window->title = strndup(title, auxilerary_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;
}
typedef void (*connection_message_handler)(struct connection* connection,
void* msg,
void* auxilerary,
size_t auxilerary_size);
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),
};
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)
{
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* auxilerary = connection->packet + handler->message_size;
size_t auxilerary_size = packet_length - handler->message_size;
handler->handler(connection, connection->packet,
auxilerary, auxilerary_size);
}
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);
}

65
display/connection.h Normal file
View File

@ -0,0 +1,65 @@
/*
* 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 "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);
void connection_can_write(struct connection* connection);
void connection_destroy(struct connection* connection);
#endif

39
display/damage-rect.c Normal file
View File

@ -0,0 +1,39 @@
/*
* 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.
*
* damage-rect.c
* Damage rectangles.
*/
#include <stddef.h>
#include "damage-rect.h"
struct damage_rect damage_rect_add(struct damage_rect a, struct damage_rect b)
{
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;
}

35
display/damage-rect.h Normal file
View File

@ -0,0 +1,35 @@
/*
* 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.
*
* damage-rect.h
* Damage rectangles.
*/
#ifndef DAMAGE_RECT_H
#define DAMAGE_RECT_H
#include <stddef.h>
struct damage_rect
{
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

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

@ -0,0 +1,680 @@
/*
* 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)*/ )
{
struct event_keyboard event;
event.window_id = display->active_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));
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;
if ( display->pointer_x < 0 )
display->pointer_x = 0;
if ( display->pointer_y < 0 )
display->pointer_y = 0;
if ( display->screen_width <= (size_t) display->pointer_x )
display->pointer_x = display->screen_width;
if ( display->screen_height <= (size_t) display->pointer_y )
display->pointer_y = display->screen_height;
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) )
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 )
{
switch ( display->mouse_state )
{
case MOUSE_STATE_NONE: break;
case MOUSE_STATE_TITLE_MOVE:
if ( window->window_state != WINDOW_STATE_REGULAR )
window_restore(window);
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);
}

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

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(0) )
{
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);
}
// 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

482
display/window.c Normal file
View File

@ -0,0 +1,482 @@
/*
* 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 : "";
size_t tt_max_width = window->width - 2 * BORDER_WIDTH;
size_t tt_desired_width = render_text_width(tt);
size_t tt_width = tt_desired_width < tt_max_width ? tt_desired_width : tt_max_width;
size_t tt_height = FONT_HEIGHT;
size_t tt_pos_x = BORDER_WIDTH + (tt_max_width - tt_width) / 2;
size_t tt_pos_y = (TITLE_HEIGHT - FONT_HEIGHT) / 2 + 2;
uint32_t tt_color = title_color;
render_text(framebuffer_crop(window->buffer, tt_pos_x, tt_pos_y, tt_width, tt_height), tt, tt_color);
size_t border_width = maximized ? BORDER_WIDTH / 2 : BORDER_WIDTH;
size_t button_size = FONT_WIDTH - 1;
size_t button_spacing = FONT_WIDTH;
struct framebuffer buttons_fb =
framebuffer_crop(window->buffer,
window->width - border_width - (FONT_HEIGHT - button_size) / 2 - 5 * button_spacing,
tt_pos_y, tt_width, tt_height);
size_t buttons_top = (FONT_HEIGHT - button_size) / 2;
for ( size_t i = 0; i < button_size; i++ )
{
framebuffer_set_pixel(buttons_fb, 0 * button_spacing + i,
buttons_top + button_size - 1, tt_color);
framebuffer_set_pixel(buttons_fb, 0 * button_spacing + i,
buttons_top + button_size - 2, tt_color);
}
for ( size_t i = 0; i < button_size; i++ )
{
framebuffer_set_pixel(buttons_fb, 2 * button_spacing + i,
buttons_top, tt_color);
framebuffer_set_pixel(buttons_fb, 2 * button_spacing + i,
buttons_top + button_size - 1 , tt_color);
framebuffer_set_pixel(buttons_fb, 2 * button_spacing,
buttons_top + i, tt_color);
framebuffer_set_pixel(buttons_fb, 2 * button_spacing + button_size - 1,
buttons_top + i, tt_color);
framebuffer_set_pixel(buttons_fb, 2 * button_spacing + i,
buttons_top + 1, tt_color);
framebuffer_set_pixel(buttons_fb, 2 * button_spacing + i,
buttons_top + button_size - 2 , tt_color);
framebuffer_set_pixel(buttons_fb, 2 * button_spacing + 1,
buttons_top + i, tt_color);
framebuffer_set_pixel(buttons_fb, 2 * button_spacing + button_size - 2,
buttons_top + i, tt_color);
}
for ( size_t i = 0; i < button_size; i++ )
{
framebuffer_set_pixel(buttons_fb, 4 * button_spacing + i,
buttons_top + i, tt_color);
framebuffer_set_pixel(buttons_fb, 4 * button_spacing + i,
buttons_top + button_size - 1 - i, tt_color);
framebuffer_set_pixel(buttons_fb, 4 * button_spacing + i + 1,
buttons_top + i, tt_color);
framebuffer_set_pixel(buttons_fb, 4 * button_spacing + i + 1,
buttons_top + button_size - 1 - i, 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;
free(window->buffer.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);
memset(window->buffer.buffer, 0, sizeof(uint32_t) * window->width * window->height);
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_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));
}

104
display/window.h Normal file
View File

@ -0,0 +1,104 @@
/*
* 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_destroy(struct window* window);
void window_drag_resize(struct window* window, int ld, int td, int wd, int hd);
void window_on_display_resolution_change(struct window* window, struct display* display);
void window_maximize(struct window* window);
void window_restore(struct window* window);
void window_toggle_maximized(struct window* window);
void window_tile(struct window* window, enum window_state state, size_t left, size_t top, size_t width, size_t height);
void window_tile_leftward(struct window* window);
void window_tile_rightward(struct window* window);
void window_tile_up(struct window* window);
void window_tile_down(struct window* window);
void window_tile_left(struct window* window);
void window_tile_right(struct window* window);
void window_tile_top(struct window* window);
void window_tile_top_left(struct window* window);
void window_tile_top_right(struct window* window);
void window_tile_bottom(struct window* window);
void window_tile_bottom_left(struct window* window);
void window_tile_bottom_right(struct window* window);
void window_notify_client_resize(struct window* window);
#endif

View File

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

View File

@ -36,7 +36,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 +64,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 +114,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 +1122,85 @@ 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);
}
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 +1211,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 uninstall 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,113 @@
/*
* 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.
*
* 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 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;
};
#endif

View File

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

859
libdisplay/include/nyan.h Normal file
View File

@ -0,0 +1,859 @@
/*
* Pop Tart Cat animation frames
* Adapted from ToAruOS.
*/
#ifndef INCLUDE_NYAN_H
#define INCLUDE_NYAN_H
#include <stdint.h>
static const char* const nyan_frame0[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&'''++'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++**''+'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'**'''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"+++#######++++++++''**''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"####################''''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"###=======########====''@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
"===;;;;;;;.=======;;;;'''@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;'***''''''''''''''''''',,,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;'**'','*',,,,,'*','**',,,,,,,,,,,,,,,,,,,,,",
";;;,,,,.,,;;;.;;;;,,,'''',,'',,,,,,,'',,'',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame1[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"+++#######++++++++'*''''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################****'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###################''**'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"###=======########==='''@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**','*',,,,,,**','**',,,,,,,,,,,,,,,,,,,,",
";;;,,.,,,,;;;;;;;;,,,,''',,,'',,,,,,''',,''',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,..,,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame2[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,..,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"##+++++++########++++++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"######################''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################'''''@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"==#######========#'****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"==================='''='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
";;=======;;;;;;;;======'@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";.;;;;;;;;;;;;;;;;;;;;;'*'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
".,.;;;;;;,,,,,,,,;;;;;;'**',**',,,,,,**','**',,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame3[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"##+++++++########++++++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"#####################'''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"==#######========##****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"=================='*'=='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
";;=======;;;;;;;;=='==='@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;;;'**','*',,,,,,'*','**',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,''',,,'',,,,,,,'',,''',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame4[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'''++'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"+++#######+++++++'**''''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"#################'****''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"##################''''*'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"###=======########==='''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;''@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;''''@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;'***'''''''''''''''''''',,,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,'**','**,,,,,,'**,'**',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,''',,,'',,,,,,,'',,''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,..,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame5[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$''$$$@@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$'**'-$$@''**',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$-$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"+++#######++++++++'*''''@$$$$$$$'*************',,,,,,,,,,,,,,,,,",
"###################****'@$$$$$$-'***.'****.'**',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"###=======########==='''@$$$$$$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$$'***''''''**',,,,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;''@@@$$$$$$$'*********',,,,,,,,,,,,,,,,,,.",
";;;;;;;;;;;;;;;;;;;;;'*''@@@@@@@@@@''''''''',,,,,,,,,,,,,,,,,,,.",
";;;;;;;;;;;;;;;;;;;;'***''''''''''''''''*',,,,,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,'**','**,,,,,,'**,'**',,,,,,,,,,,,,,,,,,..,.",
",,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame6[] = {
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,..,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&'''&&'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*''+'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'**'''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"##+++++++########++'**''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"####################''''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"==#######========#####''@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;====='''@@@@@@@@@'*********',,,,,,,,,,,.,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;'***''''''''''''''''''',,,,,,,,,,.,,,.,,,,,",
";;;;;;;;;;;;;;;;;;;;;'**'','*',,,,,'**,'**',,,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;;'''',,'',,,,,,,'',,'',,,,,,,,,,,.,,,,,.,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame7[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"##+++++++########+'*''''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################****'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###################''**'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"==#######========####'''@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;======''@@@@@@@@@@'*********',,,.,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**','*',,,,,,**','**',,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;;;''',,,'',,,,,,''',,''',,.,,,,.,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame8[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,..,...,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"+++#######++++++++#####'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"######################''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################'''''@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###=======########'****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"==================='''='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;;'@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;'*'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,,,,'**',**',,,,,,**'.'**',,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,"};
static const char* const nyan_frame9[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"+++#######++++++++#####'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"#####################'''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###=======########=****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"=================='*'=='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"===;;;;;;;========;';;;'@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,,,'**','*',,..,.**','**',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,''',,,'',,,,.,''',,''',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,.,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,.,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,"};
static const char* const nyan_frame10[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'''++'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"##+++++++########'**''''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"#################'****''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"##################''''*'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"==#######========####'''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;=====''@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;''''@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;'***'''''''''''''''''''',,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;'**'.'**..,,,,'**''**',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,''',,,'',,,,,,,''',''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame11[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$''$$$@@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$'**'-$$@''**',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$-$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"##+++++++########+'*''''@$$$$$$$'*************',,,,,,,,,,,,,,,,,",
"###################****'@$$$$$$-'***.'****.'**',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"==#######========####'''@$$$$$$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;=.===''@@@$$$$$$$'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;.'*''@@@@@@@@@@''''''''',,,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;'***''''''''''''''''*',,,,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,.;;;'**','**,,,,,,'**''**',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const* const nyan_frames[] = {
nyan_frame0,
nyan_frame1,
nyan_frame2,
nyan_frame3,
nyan_frame4,
nyan_frame5,
nyan_frame6,
nyan_frame7,
nyan_frame8,
nyan_frame9,
nyan_frame10,
nyan_frame11,
NULL
};
#define NYAN_FRAME_WIDTH 64
#define NYAN_FRAME_HEIGHT 64
static inline uint8_t nyan_palette_of_char(char c)
{
if ( c == ',' ) return 1;
if ( c == '.' ) return 2;
if ( c == '\'') return 3;
if ( c == '@' ) return 4;
if ( c == '$' ) return 5;
if ( c == '-' ) return 6;
if ( c == '>' ) return 7;
if ( c == '&' ) return 8;
if ( c == '+' ) return 9;
if ( c == '#' ) return 10;
if ( c == '=' ) return 11;
if ( c == ';' ) return 12;
if ( c == '*' ) return 13;
if ( c == '%' ) return 14;
return 0;
}
static const uint8_t nyan_palette[1+14][4] =
{
{ 0, 0, 0, 0 }, // 0; Unused entry
{ 0, 0, 97, 200 }, // 1: , = Blue background
{ 255, 255, 255, 255 }, // 2: . = White stars
{ 46, 52, 54, 255 }, // 3: ' = Black border
{ 255, 255, 215, 255 }, // 4: @ = Tan poptart
{ 215, 135, 175, 255 }, // 5: $ = Pink potart
{ 215, 0, 135, 255 }, // 6: - = Red poptart
{ 239, 41, 41, 255 }, // 7: > = Red rainbow
{ 255, 95, 0, 255 }, // 8: & = Orange rainbow
{ 252, 233, 79, 255 }, // 9: + = Yellow rainbow
{ 138, 226, 52, 255 }, // 10: # = Green rainbow
{ 0, 135, 255, 255 }, // 11: = = Light blue rainbow
{ 0, 0, 175, 255 }, // 12: ; = Dark blue rainbow
{ 85, 87, 83, 255 }, // 13: * = Grey cat face
{ 85, 87, 83, 255 }, // 14: % = Pink cheeks
};
#endif

318
libdisplay/libdisplay.c Normal file
View File

@ -0,0 +1,318 @@
/*
* 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.
*
* 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* auxilerary, size_t auxilerary_size)
{
struct display_packet_header header;
header.message_id = message_id;
header.message_length = message_size + auxilerary_size;
writeall(connection->fd, &header, sizeof(header));
writeall(connection->fd, message, message_size);
writeall(connection->fd, auxilerary, auxilerary_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));
}
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);
}
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

41
libui/include/vgafont.h Normal file
View File

@ -0,0 +1,41 @@
/*
* 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.h
* VGA font rendering.
*/
#ifndef VGAFONT_H
#define VGAFONT_H
#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

38
libui/pixel.c Normal file
View File

@ -0,0 +1,38 @@
/*
* 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.c
* Pixel functions.
*/
#include <stdint.h>
#include "pixel.h"
uint32_t blend_pixel(uint32_t bg_value, uint32_t fg_value)
{
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);
}

2
nyan/.gitignore vendored Normal file
View File

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

33
nyan/Makefile Normal file
View File

@ -0,0 +1,33 @@
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=nyan
OBJS=\
nyan.o \
LIBS:=-lui -ldisplay
all: $(PROGRAM)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(PROGRAM) $(DESTDIR)$(BINDIR)
$(PROGRAM): $(OBJS)
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $(OBJS) -o $@ $(LIBS)
%.o: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
clean:
rm -f $(PROGRAM) *.o

181
nyan/nyan.c Normal file
View File

@ -0,0 +1,181 @@
/*
* 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.
*
* nyan.c
* Window with animated nyancat.
*/
#include <sys/socket.h>
#include <sys/keycodes.h>
#include <sys/un.h>
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <error.h>
#include <ioleast.h>
#include <locale.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <timespec.h>
#include <unistd.h>
#include <display.h>
#include <nyan.h>
#include "pixel.h"
uint32_t WINDOW_ID = 0;
uint32_t WINDOW_WIDTH = 0;
uint32_t WINDOW_HEIGHT = 0;
bool need_redraw = true;
bool need_show = true;
bool need_exit = false;
void on_disconnect(void* ctx)
{
(void) ctx;
need_exit = true;
}
void on_quit(void* ctx, uint32_t window_id)
{
(void) ctx;
if ( window_id != WINDOW_ID )
return;
need_exit = true;
}
void on_resize(void* ctx, uint32_t window_id, uint32_t width, uint32_t height)
{
(void) ctx;
if ( window_id != WINDOW_ID )
return;
need_redraw = true;
WINDOW_WIDTH = width;
WINDOW_HEIGHT = height;
}
void on_keyboard(void* ctx, uint32_t window_id, uint32_t codepoint)
{
(void) ctx;
if ( window_id != WINDOW_ID )
return;
(void) codepoint;
}
int main(int argc, char* argv[])
{
(void) argc;
(void) argv;
setlocale(LC_ALL, "");
setvbuf(stdout, NULL, _IOLBF, 0);
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");
WINDOW_WIDTH = 600;
WINDOW_HEIGHT = 600;
display_create_window(connection, WINDOW_ID);
display_resize_window(connection, WINDOW_ID, WINDOW_WIDTH, WINDOW_HEIGHT);
display_title_window(connection, WINDOW_ID, "Nyanyanyanyanyanyanya...");
struct timespec frame_duration = timespec_make(0, 90 * 1000 * 1000);
struct timespec last_frame;
clock_gettime(CLOCK_MONOTONIC, &last_frame);
int frame_num = 0;
while ( !need_exit )
{
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec since_last_frame = timespec_sub(now, last_frame);
if ( !need_redraw && timespec_lt(since_last_frame, frame_duration) )
{
struct timespec remainder = timespec_sub(frame_duration, since_last_frame);
if ( timespec_lt(remainder, timespec_make(0, 10 * 1000 * 1000)) )
remainder = timespec_make(0, 10 * 1000 * 1000);
nanosleep(&remainder, NULL);
continue;
}
while ( timespec_le(frame_duration, since_last_frame) )
{
if ( !nyan_frames[++frame_num] )
frame_num = 0;
need_redraw = true;
since_last_frame = timespec_sub(since_last_frame, frame_duration);
}
if ( need_redraw )
{
last_frame = now;
uint32_t* framebuffer = (uint32_t*) malloc(sizeof(uint32_t) * WINDOW_WIDTH * WINDOW_HEIGHT);
const char* const* frame = nyan_frames[frame_num];
for ( size_t y = 0; y < WINDOW_HEIGHT; y++ )
{
int yi = y * NYAN_FRAME_HEIGHT / WINDOW_HEIGHT;
const char* line = frame[yi];
for ( size_t x = 0; x < WINDOW_WIDTH; x++ )
{
int xi = x * NYAN_FRAME_WIDTH / WINDOW_WIDTH;
char elem = line[xi];
const uint8_t* cc = nyan_palette[nyan_palette_of_char(elem)];
framebuffer[y * WINDOW_WIDTH + x] = make_color_a(cc[0], cc[1], cc[2], cc[3]);;
}
}
display_render_window(connection, WINDOW_ID, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, framebuffer);
free(framebuffer);
need_redraw = false;
}
if ( need_show )
{
display_show_window(connection, WINDOW_ID);
need_show = false;
}
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 );
}
display_disconnect(connection);
return 0;
}

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

@ -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 8
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 8
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 programs.
.Ss Trianglix
.Xr trianglix 1
can be selected as the user's triangle environment with this executable

View File

@ -385,6 +385,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 ,

View File

@ -513,6 +513,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
.Sh SEE ALSO
.Xr xorriso 1 ,
.Xr development 7 ,

View File

@ -59,13 +59,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 "execute.h"
#include "interactive.h"
@ -237,3 +239,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

@ -346,6 +346,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)
{
@ -374,6 +375,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);
}
int main(void)
@ -487,6 +496,7 @@ int main(void)
install_configurationf("upgrade.conf", "a", "src = yes\n");
// TODO: GUI support.
bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0);
while ( kblayout_setable )
{
@ -847,7 +857,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") )
@ -858,12 +868,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;
}
@ -1199,6 +1211,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 "
@ -1440,14 +1473,16 @@ int main(void)
while ( true )
{
prompt(input, sizeof(input), "finally",
"What now? (poweroff/reboot/halt/boot)", "boot");
if ( !strcasecmp(input, "poweroff") )
"What now? (exit/poweroff/reboot/halt/boot)", "boot");
if ( !strcasecmp(input, "exit") )
exit(0);
if ( !strcasecmp(input, "reboot") )
exit(1);
if ( !strcasecmp(input, "halt") )
exit(2);
if ( !strcasecmp(input, "boot") )
else if ( !strcasecmp(input, "poweroff") )
exit_gui(0);
else if ( !strcasecmp(input, "reboot") )
exit_gui(1);
else if ( !strcasecmp(input, "halt") )
exit_gui(2);
else if ( !strcasecmp(input, "boot") )
{
unmount_all_but_root();
unsetenv("SYSINSTALL_TARGET");

View File

@ -71,6 +71,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,
@ -327,6 +328,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);
}
int main(void)
@ -778,7 +787,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") )
@ -791,12 +800,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
@ -934,12 +945,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

33
terminal/Makefile Normal file
View File

@ -0,0 +1,33 @@
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
OBJS=\
terminal.o \
LIBS:=-lui -ldisplay
all: $(PROGRAM)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(PROGRAM) $(DESTDIR)$(BINDIR)
$(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

1294
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 ;;
@ -148,6 +151,7 @@ mkdir -p -- "$directory/boot/grub"
printf 'timeout="%s"\n' "$timeout"
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
@ -343,6 +365,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

@ -15,7 +15,7 @@ LIBS:=-ldispd
all: $(BINARY)
.PHONY: all install uninstall clean
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(BINDIR)

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

1
video-player/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
video-player

26
video-player/Makefile Normal file
View File

@ -0,0 +1,26 @@
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=-g -O2
CXXFLAGS?=$(OPTLEVEL)
CPPFLAGS:=$(CPPFLAGS)
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti -fcheck-new
BINARY:=video-player
LIBS:=-lswscale -lavformat -lavcodec -lavutil -ldisplay
all: $(BINARY)
.PHONY: all install clean
%: %.cpp
$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) $< -o $@ $(LIBS)
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARY) $(DESTDIR)$(BINDIR)
clean:
rm -f $(BINARY)

View File

@ -0,0 +1,5 @@
tix.version=1
tix.class=srctix
pkg.name=video-player
pkg.build-libraries=libav
pkg.build-system=sortix-usual-makefile

View File

@ -0,0 +1,274 @@
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#include <assert.h>
#include <errno.h>
#include <error.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <timespec.h>
#include <unistd.h>
#include <display.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
} // extern "C"
uint32_t WINDOW_ID = 0;
uint32_t WINDOW_WIDTH = 0;
uint32_t WINDOW_HEIGHT = 0;
bool need_show = true;
bool need_exit = false;
uint32_t* framebuffer = NULL;
size_t framebuffer_size = 0;
void on_disconnect(void*)
{
need_exit = true;
}
void on_quit(void*, uint32_t window_id)
{
if ( window_id != WINDOW_ID )
return;
need_exit = true;
}
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)
{
if ( window_id != WINDOW_ID )
return;
}
static void DisplayVideoFrame(AVFrame* frame, struct display_connection* connection)
{
size_t framebuffer_needed = sizeof(uint32_t) * WINDOW_WIDTH * WINDOW_HEIGHT;
if ( framebuffer_size != framebuffer_needed )
{
framebuffer = (uint32_t*) realloc(framebuffer, framebuffer_size = framebuffer_needed);
memset(framebuffer, 255, framebuffer_needed);
}
SwsContext* sws_ctx = sws_getContext(frame->width, frame->height,
(PixelFormat) frame->format, WINDOW_WIDTH,
WINDOW_HEIGHT, PIX_FMT_RGB32, SWS_BILINEAR,
NULL, NULL, NULL);
assert(sws_ctx);
uint8_t* data_arr[1] = { (uint8_t*) framebuffer };
int stride_arr[1] = { (int) (sizeof(framebuffer[0]) * WINDOW_WIDTH) };
sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, data_arr,
stride_arr);
sws_freeContext(sws_ctx);
display_render_window(connection, WINDOW_ID, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, framebuffer);
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 );
}
bool PlayVideo(const char* path, struct display_connection* connection)
{
bool ret = false;
int av_error;
AVFormatContext* format_ctx = NULL;
int video_stream_id;
int audio_stream_id;
AVStream* video_stream = NULL;
AVStream* audio_stream = NULL;
AVCodec* video_codec = NULL;
AVCodec* audio_codec = NULL;
AVCodecContext* video_codec_ctx = NULL;
AVCodecContext* audio_codec_ctx = NULL;
AVFrame* video_frame = NULL;
AVFrame* audio_frame = NULL;
AVPacket packet;
if ( (av_error = avformat_open_input(&format_ctx, path, NULL, NULL)) < 0 )
{
error(0, 0, "%s: cannot open: %i\n", path, av_error);
goto cleanup_done;
}
if ( (av_error = avformat_find_stream_info(format_ctx, NULL)) < 0 )
{
error(0, 0, "%s: avformat_find_stream_info: %i\n", path, av_error);
goto cleanup_input;
}
video_stream_id = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, -1,
-1, &video_codec, 0);
audio_stream_id = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1,
-1, &audio_codec, 0);
if ( 0 <= video_stream_id )
video_stream = format_ctx->streams[video_stream_id];
if ( 0 <= audio_stream_id )
audio_stream = format_ctx->streams[audio_stream_id];
if ( !video_stream )
{
error(0, 0, "%s: no video stream found\n", path);
goto cleanup_input;
}
if ( video_codec && !(video_codec_ctx = avcodec_alloc_context3(video_codec)))
goto cleanup_input;
if ( audio_codec && !(audio_codec_ctx = avcodec_alloc_context3(audio_codec)))
goto cleanup_video_codec_ctx;
if ( video_codec_ctx )
{
video_codec_ctx->extradata = video_stream->codec->extradata;
video_codec_ctx->extradata_size = video_stream->codec->extradata_size;
if ( (av_error = avcodec_open2(video_codec_ctx, NULL, NULL)) < 0 )
goto cleanup_audio_codec_ctx;
}
if ( audio_codec_ctx )
{
audio_codec_ctx->extradata = audio_stream->codec->extradata;
audio_codec_ctx->extradata_size = audio_stream->codec->extradata_size;
if ( (av_error = avcodec_open2(audio_codec_ctx, NULL, NULL)) < 0 )
goto cleanup_audio_codec_ctx;
}
if ( !(video_frame = avcodec_alloc_frame()) )
goto cleanup_audio_codec_ctx;
if ( !(audio_frame = avcodec_alloc_frame()) )
goto cleanup_video_frame;
struct timespec next_frame_at;
clock_gettime(CLOCK_MONOTONIC, &next_frame_at);
while ( !need_exit && 0 <= (av_error = av_read_frame(format_ctx, &packet)) )
{
int stream_index = packet.stream_index;
int packet_off = 0;
while ( stream_index == video_stream->index && packet_off < packet.size )
{
packet.data += packet_off; packet.size -= packet_off;
int got_frame;
int bytes_used = avcodec_decode_video2(video_codec_ctx, video_frame,
&got_frame, &packet);
packet.data -= packet_off; packet.size += packet_off;
if ( (av_error = bytes_used) < 0 )
goto break_decode_loop;
if ( !got_frame )
break;
packet_off += bytes_used;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
while ( timespec_le(now, next_frame_at) )
{
struct timespec left = timespec_sub(next_frame_at, now);
clock_nanosleep(CLOCK_MONOTONIC, 0, &left, NULL);
clock_gettime(CLOCK_MONOTONIC, &now);
}
DisplayVideoFrame(video_frame, connection);
uintmax_t usecs = video_codec_ctx->ticks_per_frame * 1000000 *
video_codec_ctx->time_base.num /
video_codec_ctx->time_base.den;
next_frame_at = timespec_add(next_frame_at, timespec_make(0, usecs * 1000));
}
while ( stream_index == audio_stream->index && packet_off < packet.size )
{
// TODO: Add sound support when an backend is available.
packet_off = packet.size;
}
}
break_decode_loop:
// TODO: Determine whether the are here because of EOF or whether an error
// occured and we need to print an error.
// TODO: Do we need to clean up the last packet or does the av_read_frame
// function do that for us upon error?
ret = true;
goto cleanup_audio_frame;
cleanup_audio_frame:
if ( audio_frame )
#if 55 <= LIBAVCODEC_VERSION_MAJOR
avcodec_free_frame(&audio_frame);
#else
av_free(audio_frame);
#endif
cleanup_video_frame:
if ( video_frame )
#if 55 <= LIBAVCODEC_VERSION_MAJOR
avcodec_free_frame(&video_frame);
#else
av_free(video_frame);
#endif
cleanup_audio_codec_ctx:
if ( audio_codec_ctx )
{
audio_codec_ctx->extradata = NULL;
avcodec_close(audio_codec_ctx);
av_free(audio_codec_ctx);
}
cleanup_video_codec_ctx:
if ( video_codec_ctx )
{
video_codec_ctx->extradata = NULL;
avcodec_close(video_codec_ctx);
av_free(video_codec_ctx);
}
cleanup_input:
avformat_close_input(&format_ctx);
cleanup_done:
return ret;
}
int main(int argc, char* argv[])
{
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");
av_register_all();
WINDOW_WIDTH = 800;
WINDOW_HEIGHT = 450;
display_create_window(connection, WINDOW_ID);
display_resize_window(connection, WINDOW_ID, WINDOW_WIDTH, WINDOW_HEIGHT);
display_show_window(connection, WINDOW_ID);
for ( int i = 1; i < argc; i++ )
{
display_title_window(connection, WINDOW_ID, argv[i]);
if ( !PlayVideo(argv[i], connection) )
return 1;
}
display_disconnect(connection);
return 0;
}