562 lines
13 KiB
C++
562 lines
13 KiB
C++
#include "Base.h"
|
|
#include "Definitions.h"
|
|
#include "Extensions.h"
|
|
#include "Keymap.h"
|
|
#include "Utils.h"
|
|
|
|
#include <X11/X.h>
|
|
#include <X11/Xatom.h>
|
|
|
|
#include <signal.h>
|
|
#include <sys/epoll.h>
|
|
#include <sys/stat.h>
|
|
|
|
#define USE_UNIX_SOCKET 0
|
|
|
|
#if USE_UNIX_SOCKET
|
|
#include <sys/un.h>
|
|
#else
|
|
#include <netinet/in.h>
|
|
#endif
|
|
|
|
const xPixmapFormat g_formats[6] {
|
|
{
|
|
.depth = 1,
|
|
.bitsPerPixel = 1,
|
|
.scanLinePad = 32,
|
|
},
|
|
{
|
|
.depth = 4,
|
|
.bitsPerPixel = 4,
|
|
.scanLinePad = 32,
|
|
},
|
|
{
|
|
.depth = 8,
|
|
.bitsPerPixel = 8,
|
|
.scanLinePad = 32,
|
|
},
|
|
{
|
|
.depth = 16,
|
|
.bitsPerPixel = 16,
|
|
.scanLinePad = 32,
|
|
},
|
|
{
|
|
.depth = 24,
|
|
.bitsPerPixel = 32,
|
|
.scanLinePad = 32,
|
|
},
|
|
{
|
|
.depth = 32,
|
|
.bitsPerPixel = 32,
|
|
.scanLinePad = 32,
|
|
}
|
|
};
|
|
|
|
const xDepth g_depth {
|
|
.depth = 24,
|
|
.nVisuals = 1,
|
|
};
|
|
|
|
const xVisualType g_visual {
|
|
.visualID = 1,
|
|
.c_class = TrueColor,
|
|
.bitsPerRGB = 8,
|
|
.colormapEntries = 256,
|
|
.redMask = 0xFF0000,
|
|
.greenMask = 0x00FF00,
|
|
.blueMask = 0x0000FF,
|
|
};
|
|
|
|
const xWindowRoot g_root {
|
|
.windowId = 2,
|
|
.defaultColormap = 0,
|
|
.whitePixel = 0xFFFFFF,
|
|
.blackPixel = 0x000000,
|
|
.currentInputMask = 0,
|
|
.pixWidth = 1280,
|
|
.pixHeight = 800,
|
|
.mmWidth = 1280,
|
|
.mmHeight = 800,
|
|
.minInstalledMaps = 1,
|
|
.maxInstalledMaps = 1,
|
|
.rootVisualID = g_visual.visualID,
|
|
.backingStore = 0,
|
|
.saveUnders = 0,
|
|
.rootDepth = 24,
|
|
.nDepths = 1,
|
|
};
|
|
|
|
BAN::HashMap<CARD32, BAN::UniqPtr<Object>> g_objects;
|
|
|
|
BAN::HashMap<BAN::String, ATOM> g_atoms_name_to_id;
|
|
BAN::HashMap<ATOM, BAN::String> g_atoms_id_to_name;
|
|
ATOM g_atom_value = XA_LAST_PREDEFINED + 1;
|
|
|
|
int g_epoll_fd;
|
|
BAN::HashMap<int, EpollThingy> g_epoll_thingies;
|
|
|
|
BAN::ErrorOr<void> extension_bigrequests(Client& client_info, BAN::ConstByteSpan packet)
|
|
{
|
|
switch (packet[1])
|
|
{
|
|
case 0:
|
|
{
|
|
xGenericReply reply {
|
|
.type = X_Reply,
|
|
.sequenceNumber = client_info.sequence,
|
|
.length = 0,
|
|
.data00 = (16 << 20) / 4, // 16 MiB
|
|
};
|
|
TRY(encode(client_info.output_buffer, reply));
|
|
|
|
client_info.has_bigrequests = true;
|
|
|
|
dprintln("client enabled big requests");
|
|
|
|
break;
|
|
}
|
|
default:
|
|
dwarnln("invalid BIG-REQUESTS minor opcode {}", packet[1]);
|
|
return BAN::Error::from_errno(EINVAL);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
int main()
|
|
{
|
|
for (int sig = 1; sig < NSIG; sig++)
|
|
if (sig != SIGWINCH)
|
|
signal(sig, exit);
|
|
|
|
#if USE_UNIX_SOCKET
|
|
if (mkdir("/tmp/.X11-unix", 01777) == -1 && errno != EEXIST)
|
|
{
|
|
perror("xbanan: mkdir");
|
|
return 1;
|
|
}
|
|
|
|
int server_sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
#else
|
|
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
#endif
|
|
if (server_sock == -1)
|
|
{
|
|
perror("xbanan: socket");
|
|
return 1;
|
|
}
|
|
|
|
#if USE_UNIX_SOCKET
|
|
const sockaddr_un addr {
|
|
.sun_family = AF_UNIX,
|
|
.sun_path = "/tmp/.X11-unix/X69"
|
|
};
|
|
#else
|
|
const sockaddr_in addr {
|
|
.sin_family = AF_INET,
|
|
.sin_port = htons(6069),
|
|
.sin_addr = { htonl(INADDR_ANY) },
|
|
};
|
|
#endif
|
|
|
|
{
|
|
int one = 1;
|
|
setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
|
|
}
|
|
|
|
if (bind(server_sock, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) == -1)
|
|
{
|
|
perror("xbanan: bind");
|
|
return 1;
|
|
}
|
|
#if USE_UNIX_SOCKET
|
|
atexit([] { unlink("/tmp/.X11-unix/X69"); });
|
|
#endif
|
|
|
|
if (listen(server_sock, SOMAXCONN) == -1)
|
|
{
|
|
perror("xbanan: listen");
|
|
return 1;
|
|
}
|
|
|
|
g_epoll_fd = epoll_create1(0);
|
|
if (g_epoll_fd == -1)
|
|
{
|
|
perror("xbanan: epoll_create1");
|
|
return 1;
|
|
}
|
|
|
|
epoll_event event { .events = EPOLLIN, .data = { .ptr = nullptr } };
|
|
if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, server_sock, &event) == -1)
|
|
{
|
|
perror("xbanan: epoll_ctl");
|
|
return 1;
|
|
}
|
|
|
|
#define APPEND_ATOM(name) do { \
|
|
MUST(g_atoms_id_to_name.insert(name, #name##_sv)); \
|
|
MUST(g_atoms_name_to_id.insert(#name##_sv, name)); \
|
|
} while (0)
|
|
APPEND_ATOM(XA_PRIMARY);
|
|
APPEND_ATOM(XA_SECONDARY);
|
|
APPEND_ATOM(XA_ARC);
|
|
APPEND_ATOM(XA_ATOM);
|
|
APPEND_ATOM(XA_BITMAP);
|
|
APPEND_ATOM(XA_CARDINAL);
|
|
APPEND_ATOM(XA_COLORMAP);
|
|
APPEND_ATOM(XA_CURSOR);
|
|
APPEND_ATOM(XA_CUT_BUFFER0);
|
|
APPEND_ATOM(XA_CUT_BUFFER1);
|
|
APPEND_ATOM(XA_CUT_BUFFER2);
|
|
APPEND_ATOM(XA_CUT_BUFFER3);
|
|
APPEND_ATOM(XA_CUT_BUFFER4);
|
|
APPEND_ATOM(XA_CUT_BUFFER5);
|
|
APPEND_ATOM(XA_CUT_BUFFER6);
|
|
APPEND_ATOM(XA_CUT_BUFFER7);
|
|
APPEND_ATOM(XA_DRAWABLE);
|
|
APPEND_ATOM(XA_FONT);
|
|
APPEND_ATOM(XA_INTEGER);
|
|
APPEND_ATOM(XA_PIXMAP);
|
|
APPEND_ATOM(XA_POINT);
|
|
APPEND_ATOM(XA_RECTANGLE);
|
|
APPEND_ATOM(XA_RESOURCE_MANAGER);
|
|
APPEND_ATOM(XA_RGB_COLOR_MAP);
|
|
APPEND_ATOM(XA_RGB_BEST_MAP);
|
|
APPEND_ATOM(XA_RGB_BLUE_MAP);
|
|
APPEND_ATOM(XA_RGB_DEFAULT_MAP);
|
|
APPEND_ATOM(XA_RGB_GRAY_MAP);
|
|
APPEND_ATOM(XA_RGB_GREEN_MAP);
|
|
APPEND_ATOM(XA_RGB_RED_MAP);
|
|
APPEND_ATOM(XA_STRING);
|
|
APPEND_ATOM(XA_VISUALID);
|
|
APPEND_ATOM(XA_WINDOW);
|
|
APPEND_ATOM(XA_WM_COMMAND);
|
|
APPEND_ATOM(XA_WM_HINTS);
|
|
APPEND_ATOM(XA_WM_CLIENT_MACHINE);
|
|
APPEND_ATOM(XA_WM_ICON_NAME);
|
|
APPEND_ATOM(XA_WM_ICON_SIZE);
|
|
APPEND_ATOM(XA_WM_NAME);
|
|
APPEND_ATOM(XA_WM_NORMAL_HINTS);
|
|
APPEND_ATOM(XA_WM_SIZE_HINTS);
|
|
APPEND_ATOM(XA_WM_ZOOM_HINTS);
|
|
APPEND_ATOM(XA_MIN_SPACE);
|
|
APPEND_ATOM(XA_NORM_SPACE);
|
|
APPEND_ATOM(XA_MAX_SPACE);
|
|
APPEND_ATOM(XA_END_SPACE);
|
|
APPEND_ATOM(XA_SUPERSCRIPT_X);
|
|
APPEND_ATOM(XA_SUPERSCRIPT_Y);
|
|
APPEND_ATOM(XA_SUBSCRIPT_X);
|
|
APPEND_ATOM(XA_SUBSCRIPT_Y);
|
|
APPEND_ATOM(XA_UNDERLINE_POSITION);
|
|
APPEND_ATOM(XA_UNDERLINE_THICKNESS);
|
|
APPEND_ATOM(XA_STRIKEOUT_ASCENT);
|
|
APPEND_ATOM(XA_STRIKEOUT_DESCENT);
|
|
APPEND_ATOM(XA_ITALIC_ANGLE);
|
|
APPEND_ATOM(XA_X_HEIGHT);
|
|
APPEND_ATOM(XA_QUAD_WIDTH);
|
|
APPEND_ATOM(XA_WEIGHT);
|
|
APPEND_ATOM(XA_POINT_SIZE);
|
|
APPEND_ATOM(XA_RESOLUTION);
|
|
APPEND_ATOM(XA_COPYRIGHT);
|
|
APPEND_ATOM(XA_NOTICE);
|
|
APPEND_ATOM(XA_FONT_NAME);
|
|
APPEND_ATOM(XA_FAMILY_NAME);
|
|
APPEND_ATOM(XA_FULL_NAME);
|
|
APPEND_ATOM(XA_CAP_HEIGHT);
|
|
APPEND_ATOM(XA_WM_CLASS);
|
|
APPEND_ATOM(XA_WM_TRANSIENT_FOR);
|
|
#undef APPEND_ATOM
|
|
|
|
MUST(initialize_keymap());
|
|
|
|
install_extension("BIG-REQUESTS"_sv, extension_bigrequests);
|
|
|
|
printf("xbanan started\n");
|
|
|
|
const auto close_client =
|
|
[](int client_fd)
|
|
{
|
|
auto& epoll_thingy = g_epoll_thingies[client_fd];
|
|
ASSERT(epoll_thingy.type == EpollThingy::Type::Client);
|
|
auto& client_info = epoll_thingy.value.get<Client>();
|
|
|
|
dprintln("client {} disconnected", client_fd);
|
|
|
|
for (auto id : client_info.objects)
|
|
{
|
|
auto it = g_objects.find(id);
|
|
if (it == g_objects.end())
|
|
continue;
|
|
|
|
auto& object = *it->value;
|
|
if (object.type == Object::Type::Window)
|
|
{
|
|
auto& window = object.object.get<Object::Window>();
|
|
if (window.window.has<BAN::UniqPtr<LibGUI::Window>>())
|
|
{
|
|
auto& gui_window = window.window.get<BAN::UniqPtr<LibGUI::Window>>();
|
|
epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, gui_window->server_fd(), nullptr);
|
|
g_epoll_thingies.remove(gui_window->server_fd());
|
|
}
|
|
}
|
|
|
|
g_objects.remove(it);
|
|
}
|
|
|
|
epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
|
|
close(client_fd);
|
|
g_epoll_thingies.remove(client_fd);
|
|
};
|
|
|
|
MUST(g_objects.insert(g_root.windowId,
|
|
MUST(BAN::UniqPtr<Object>::create(Object {
|
|
.type = Object::Type::Window,
|
|
.object = Object::Window {
|
|
.event_mask = 0,
|
|
.c_class = InputOutput,
|
|
.window = {},
|
|
}
|
|
}))
|
|
));
|
|
|
|
MUST(g_objects.insert(g_visual.visualID,
|
|
MUST(BAN::UniqPtr<Object>::create(Object {
|
|
.type = Object::Type::Visual,
|
|
}))
|
|
));
|
|
|
|
for (;;)
|
|
{
|
|
epoll_event events[16];
|
|
const int event_count = epoll_wait(g_epoll_fd, events, 16, -1);
|
|
|
|
for (int i = 0; i < event_count; i++)
|
|
{
|
|
if (events[i].data.ptr == nullptr)
|
|
{
|
|
int client_sock = accept(server_sock, nullptr, nullptr);
|
|
if (client_sock == -1)
|
|
{
|
|
perror("xbanan: accept");
|
|
continue;
|
|
}
|
|
|
|
MUST(g_epoll_thingies.insert(client_sock, {
|
|
.type = EpollThingy::Type::Client,
|
|
.value = Client {
|
|
.fd = client_sock,
|
|
.state = Client::State::ConnectionSetup,
|
|
}
|
|
}));
|
|
|
|
epoll_event event = { .events = EPOLLIN, .data = { .fd = client_sock } };
|
|
if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, client_sock, &event) == -1)
|
|
{
|
|
perror("xbanan: epoll_ctl");
|
|
close(client_sock);
|
|
continue;
|
|
}
|
|
|
|
dprintln("client {} connected", client_sock);
|
|
continue;
|
|
}
|
|
|
|
auto it = g_epoll_thingies.find(events[i].data.fd);
|
|
if (it == g_epoll_thingies.end())
|
|
continue;
|
|
auto& epoll_thingy = it->value;
|
|
|
|
if (epoll_thingy.type == EpollThingy::Type::Window)
|
|
{
|
|
auto* window = epoll_thingy.value.get<LibGUI::Window*>();
|
|
window->poll_events();
|
|
continue;
|
|
}
|
|
|
|
ASSERT(epoll_thingy.type == EpollThingy::Type::Client);
|
|
|
|
auto& client_info = epoll_thingy.value.get<Client>();
|
|
|
|
if (events[i].events & EPOLLHUP)
|
|
{
|
|
close_client(client_info.fd);
|
|
continue;
|
|
}
|
|
|
|
if (events[i].events & EPOLLOUT)
|
|
{
|
|
const ssize_t nsend = send(
|
|
client_info.fd,
|
|
client_info.output_buffer.data(),
|
|
client_info.output_buffer.size(),
|
|
0
|
|
);
|
|
|
|
if (nsend == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
|
goto send_done;
|
|
|
|
if (nsend < 0)
|
|
perror("xbanan: send");
|
|
if (nsend <= 0)
|
|
{
|
|
close_client(client_info.fd);
|
|
continue;
|
|
}
|
|
|
|
memmove(
|
|
client_info.output_buffer.data(),
|
|
client_info.output_buffer.data() + nsend,
|
|
client_info.output_buffer.size() - nsend
|
|
);
|
|
MUST(client_info.output_buffer.resize(client_info.output_buffer.size() - nsend));
|
|
|
|
if (client_info.output_buffer.empty())
|
|
{
|
|
epoll_event event { .events = EPOLLIN, .data = { .fd = client_info.fd } };
|
|
if (epoll_ctl(g_epoll_fd, EPOLL_CTL_MOD, client_info.fd, &event) == -1)
|
|
{
|
|
perror("xbanan: epoll_ctl");
|
|
close_client(client_info.fd);
|
|
continue;
|
|
}
|
|
|
|
client_info.has_epollout = false;
|
|
}
|
|
|
|
send_done:
|
|
(void)0;
|
|
}
|
|
|
|
if (!(events[i].events & EPOLLIN))
|
|
continue;
|
|
|
|
switch (client_info.state)
|
|
{
|
|
case Client::State::ConnectionSetup:
|
|
{
|
|
xConnClientPrefix client_prefix;
|
|
|
|
const ssize_t nrecv = recv(client_info.fd, &client_prefix, sizeof(client_prefix), 0);
|
|
if (nrecv < 0)
|
|
perror("xbanan: recv");
|
|
if (nrecv <= 0)
|
|
{
|
|
close_client(client_info.fd);
|
|
continue;
|
|
}
|
|
|
|
const size_t auth_string_len = (client_prefix.nbytesAuthString + 3) / 4 * 4;
|
|
const size_t auth_proto_len = (client_prefix.nbytesAuthProto + 3) / 4 * 4;
|
|
const size_t auth_len = auth_string_len + auth_proto_len;
|
|
|
|
size_t auth_received = 0;
|
|
while (auth_received < auth_len)
|
|
{
|
|
char buffer[128];
|
|
|
|
const size_t to_recv = BAN::Math::min(sizeof(buffer), auth_len - auth_received);
|
|
const ssize_t nrecv = recv(client_info.fd, buffer, to_recv, 0);
|
|
if (nrecv < 0)
|
|
perror("xbanan: recv");
|
|
if (nrecv <= 0)
|
|
{
|
|
close_client(client_info.fd);
|
|
continue;
|
|
}
|
|
auth_received += nrecv;
|
|
}
|
|
|
|
ASSERT(nrecv == sizeof(client_prefix));
|
|
|
|
if (auto ret = setup_client_conneciton(client_info, client_prefix); ret.is_error())
|
|
{
|
|
dwarnln("setup_client_connection: {}", ret.error());
|
|
close_client(client_info.fd);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case Client::State::Connected:
|
|
{
|
|
char buffer[1024] {};
|
|
|
|
const ssize_t nrecv = recv(client_info.fd, buffer, sizeof(buffer), 0);
|
|
if (nrecv < 0)
|
|
perror("xbanan: recv");
|
|
if (nrecv <= 0)
|
|
{
|
|
close_client(client_info.fd);
|
|
continue;
|
|
}
|
|
|
|
const size_t old_size = client_info.input_buffer.size();
|
|
MUST(client_info.input_buffer.resize(old_size + nrecv));
|
|
memcpy(client_info.input_buffer.data() + old_size, buffer, nrecv);
|
|
|
|
for (;;)
|
|
{
|
|
if (client_info.input_buffer.size() < 4)
|
|
break;
|
|
|
|
bool is_big_request = false;
|
|
uint32_t byte_length = 4 * *reinterpret_cast<const uint16_t*>(client_info.input_buffer.data() + 2);
|
|
|
|
if (byte_length == 0 && client_info.has_bigrequests)
|
|
{
|
|
if (client_info.input_buffer.size() < 8)
|
|
break;
|
|
byte_length = 4 * *reinterpret_cast<const uint32_t*>(client_info.input_buffer.data() + 4);
|
|
is_big_request = true;
|
|
}
|
|
|
|
if (client_info.input_buffer.size() < byte_length)
|
|
break;
|
|
|
|
auto packet = BAN::ConstByteSpan(client_info.input_buffer.span()).slice(0, byte_length);
|
|
if (is_big_request)
|
|
{
|
|
auto* input_u32 = reinterpret_cast<uint32_t*>(client_info.input_buffer.data());
|
|
input_u32[1] = input_u32[0];
|
|
packet = packet.slice(4);
|
|
}
|
|
|
|
if (auto ret = handle_packet(client_info, packet); ret.is_error() && ret.error().get_error_code() != ENOENT)
|
|
{
|
|
dwarnln("handle_packet: {}", ret.error());
|
|
close_client(client_info.fd);
|
|
continue;
|
|
}
|
|
|
|
memmove(client_info.input_buffer.data(), client_info.input_buffer.data() + byte_length, client_info.input_buffer.size() - byte_length);
|
|
MUST(client_info.input_buffer.resize(client_info.input_buffer.size() - byte_length));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
iterator_invalidated:
|
|
for (auto& [_, thingy] : g_epoll_thingies)
|
|
{
|
|
if (thingy.type != EpollThingy::Type::Client)
|
|
continue;
|
|
|
|
auto& client_info = thingy.value.get<Client>();
|
|
if (client_info.output_buffer.empty() || client_info.has_epollout)
|
|
continue;
|
|
|
|
epoll_event event { .events = EPOLLIN | EPOLLOUT, .data = { .fd = client_info.fd } };
|
|
if (epoll_ctl(g_epoll_fd, EPOLL_CTL_MOD, client_info.fd, &event) == -1)
|
|
{
|
|
perror("xbanan: epoll_ctl");
|
|
close_client(client_info.fd);
|
|
goto iterator_invalidated;
|
|
}
|
|
|
|
client_info.has_epollout = true;
|
|
}
|
|
}
|
|
}
|