All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m33s
595 lines
21 KiB
C++
595 lines
21 KiB
C++
#include <cstring>
|
|
#include <thread>
|
|
#include <ReWindow/types/Window.h>
|
|
#include <ReWindow/types/Cursors.h>
|
|
#include <ReWindow/Logger.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xatom.h>
|
|
#include <thread>
|
|
#include <dbus/dbus.h>
|
|
|
|
class ReWindow::RWindow::Platform {
|
|
public:
|
|
Window window;
|
|
XEvent xev;
|
|
Display* display;
|
|
int defaultScreen;
|
|
XVisualInfo* visual;
|
|
XSetWindowAttributes xSetWindowAttributes;
|
|
XWindowAttributes windowAttributes;
|
|
Atom wmDeleteWindow;
|
|
Atom windowTypeAtom;
|
|
Atom windowTypeUtilityAtom;
|
|
XSizeHints hints;
|
|
Cursor invisible_cursor = 0;
|
|
XWMHints* wm_hints = nullptr;
|
|
bool window_visible = true;
|
|
std::pair<int, int> position = { 0, 0 };
|
|
std::pair<int, int> size_before_fullscreen {0, 0};
|
|
};
|
|
|
|
|
|
using namespace ReWindow;
|
|
|
|
bool RWindow::Notify(const std::string& title, const std::string& content, const std::filesystem::path& icon) {
|
|
DBusError db_error;
|
|
dbus_error_init(&db_error);
|
|
|
|
DBusConnection* db_connection = dbus_bus_get(DBUS_BUS_SESSION, &db_error);
|
|
|
|
if (!db_connection) {
|
|
dbus_error_free(&db_error);
|
|
return false;
|
|
}
|
|
|
|
DBusMessage* db_message = dbus_message_new_method_call(
|
|
"org.freedesktop.Notifications",
|
|
"/org/freedesktop/Notifications",
|
|
"org.freedesktop.Notifications",
|
|
"Notify"
|
|
);
|
|
|
|
if (!db_message)
|
|
return false;
|
|
|
|
const char* sender = this->title.c_str();
|
|
uint32_t replaces_id = 0;
|
|
const char* app_icon = icon.c_str();
|
|
const char* summary = title.c_str();
|
|
const char* body = content.c_str();
|
|
const char** actions = nullptr;
|
|
|
|
DBusMessageIter db_args;
|
|
dbus_message_iter_init_append(db_message, &db_args);
|
|
|
|
dbus_message_iter_append_basic(&db_args, DBUS_TYPE_STRING, &sender);
|
|
dbus_message_iter_append_basic(&db_args, DBUS_TYPE_UINT32, &replaces_id);
|
|
dbus_message_iter_append_basic(&db_args, DBUS_TYPE_STRING, &app_icon);
|
|
dbus_message_iter_append_basic(&db_args, DBUS_TYPE_STRING, &summary);
|
|
dbus_message_iter_append_basic(&db_args, DBUS_TYPE_STRING, &body);
|
|
|
|
DBusMessageIter db_actions_array_iterator;
|
|
dbus_message_iter_open_container(&db_args, DBUS_TYPE_ARRAY, "s", &db_actions_array_iterator);
|
|
dbus_message_iter_close_container(&db_args, &db_actions_array_iterator);
|
|
|
|
dbus_message_iter_open_container(&db_args, DBUS_TYPE_ARRAY, "{sv}", &db_actions_array_iterator);
|
|
dbus_message_iter_close_container(&db_args, &db_actions_array_iterator);
|
|
|
|
int32_t notif_timeout = 5000;
|
|
dbus_message_iter_append_basic(&db_args, DBUS_TYPE_INT32, ¬if_timeout);
|
|
|
|
DBusMessage* db_reply = dbus_connection_send_with_reply_and_block(db_connection, db_message, -1, &db_error);
|
|
dbus_message_unref(db_message);
|
|
|
|
if (!db_reply) {
|
|
dbus_error_free(&db_error);
|
|
return false;
|
|
}
|
|
|
|
dbus_message_unref(db_reply);
|
|
return true;
|
|
}
|
|
|
|
void RWindow::SetSize(const std::pair<int, int>& size) {
|
|
this->SetSize(size.first, size.second);
|
|
}
|
|
|
|
bool RWindow::SetCursorPosition(const std::pair<int, int>& position) {
|
|
if (!IsFocused())
|
|
return false;
|
|
|
|
if (!IsVisible())
|
|
return false;
|
|
|
|
if (position.first > GetWidth() || position.first < 0)
|
|
return false;
|
|
|
|
if (position.second > GetHeight() || position.second < 0)
|
|
return false;
|
|
|
|
// TODO XWarpPointer has no way to verify it actually happened.
|
|
XWarpPointer(platform->display, None, platform->window, 0, 0, 0, 0, position.first, position.second);
|
|
return true;
|
|
}
|
|
|
|
void RWindow::SetKeyRepeatEnabled(bool state) {
|
|
key_repeat = state;
|
|
|
|
if (state)
|
|
return (void) XAutoRepeatOn(platform->display);
|
|
|
|
XAutoRepeatOff(platform->display);
|
|
}
|
|
|
|
void RWindow::Flash() {
|
|
if (IsFocused())
|
|
return;
|
|
|
|
if (platform->wm_hints == nullptr)
|
|
platform->wm_hints = XAllocWMHints();
|
|
|
|
platform->wm_hints->flags = XUrgencyHint;
|
|
XSetWMHints(platform->display, platform->window, platform->wm_hints);
|
|
}
|
|
|
|
RWindow::RWindow() {
|
|
platform = new Platform();
|
|
extant = this;
|
|
}
|
|
|
|
void RWindow::SetCursorFocused(bool state) {
|
|
toggling_cursor_focus = state;
|
|
|
|
if (!state && cursor_focused) {
|
|
XUngrabPointer(platform->display, CurrentTime);
|
|
cursor_focused = state;
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::pair<int, int> RWindow::SetCursorCenter() {
|
|
auto current_pos = GetAccurateCursorCoordinates();
|
|
XWarpPointer(platform->display, None, platform->window, 0, 0, 0, 0, width / 2, height / 2);
|
|
return current_pos;
|
|
}
|
|
|
|
RWindow::RWindow(const std::string& wTitle, int wWidth, int wHeight, bool wFullscreen, bool wResizable, bool wVsync)
|
|
: title(wTitle), width(wWidth), height(wHeight), fullscreen_mode(wFullscreen), resizable(wResizable), vsync(wVsync)
|
|
{ platform = new Platform(); extant = this; }
|
|
|
|
void RWindow::Raise() {
|
|
Logger::Debug(std::format("Raising window '{}'", this->title));
|
|
// Get the position of the renderable area relative to the rest of the window.
|
|
XGetWindowAttributes(platform->display, platform->window, &platform->windowAttributes);
|
|
XRaiseWindow(platform->display, platform->window);
|
|
}
|
|
|
|
void RWindow::DialogOK(const std::string& window_title, const std::string& message) {
|
|
int padding = 10;
|
|
|
|
XFontStruct* font = XLoadQueryFont(platform->display, "6x13");
|
|
int text_width = XTextWidth(font, message.c_str(), message.size());
|
|
int button_text_width = XTextWidth(font, "OK", 2);
|
|
std::pair<int, int> button_size = {80, 30};
|
|
std::pair<int, int> dimensions = { std::max(text_width + 2 * padding, button_size.first + 2 * padding), 82};
|
|
std::pair<int, int> text_pos = {(dimensions.first - text_width) / 2, padding + font->ascent};
|
|
std::pair<int, int> button_pos = { (dimensions.first - button_size.first) / 2, text_pos.second + font->ascent + padding };
|
|
|
|
Window window = XCreateSimpleWindow(platform->display, RootWindow(platform->display, platform->defaultScreen),
|
|
100, 100, dimensions.first, dimensions.second, 1,
|
|
BlackPixel(platform->display, platform->defaultScreen),
|
|
WhitePixel(platform->display, platform->defaultScreen));
|
|
|
|
XStoreName(platform->display, window, window_title.c_str());
|
|
XSelectInput(platform->display, window, ExposureMask | ButtonPressMask | StructureNotifyMask);
|
|
|
|
// No resizing.
|
|
XSizeHints hints;
|
|
hints.flags = PMinSize | PMaxSize;
|
|
hints.min_width = hints.max_width = dimensions.first;
|
|
hints.min_height = hints.max_height = dimensions.second;
|
|
XSetWMNormalHints(platform->display, window, &hints);
|
|
|
|
Atom wm_delete_window = XInternAtom(platform->display, "WM_DELETE_WINDOW", False);
|
|
XSetWMProtocols(platform->display, window, &wm_delete_window, 1);
|
|
|
|
XMapWindow(platform->display, window);
|
|
GC gc = XCreateGC(platform->display, window, 0, nullptr);
|
|
XSetForeground(platform->display, gc, BlackPixel(platform->display, platform->defaultScreen));
|
|
|
|
XEvent event;
|
|
while (true) {
|
|
XNextEvent(platform->display, &event);
|
|
|
|
if (event.type == Expose) {
|
|
XDrawString(platform->display, window, gc, text_pos.first, text_pos.second, message.c_str(), message.size());
|
|
XDrawString(platform->display, window, gc, button_pos.first + (button_size.first - button_text_width) / 2, button_pos.second + 20, "OK", 2);
|
|
XDrawRectangle(platform->display, window, gc, button_pos.first, button_pos.second, button_size.first, button_size.second);
|
|
}
|
|
|
|
else if (event.type == ClientMessage) {
|
|
if (event.xclient.message_type == XInternAtom(platform->display, "WM_PROTOCOLS", False) &&
|
|
static_cast<Atom>(event.xclient.data.l[0]) == wm_delete_window)
|
|
break;
|
|
}
|
|
|
|
else if (event.type == ButtonPress) {
|
|
if (event.xbutton.x >= button_pos.first && event.xbutton.x <= button_pos.first + button_size.first
|
|
&& event.xbutton.y >= button_pos.second && event.xbutton.y <= button_pos.second + button_size.second)
|
|
break;
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
}
|
|
|
|
XFreeFont(platform->display, font);
|
|
XFreeGC(platform->display, gc);
|
|
XDestroyWindow(platform->display, window);
|
|
}
|
|
void RWindow::Lower()
|
|
{
|
|
Logger::Debug(std::format("Lowering window '{}'", this->title));
|
|
XLowerWindow(platform->display, platform->window);
|
|
}
|
|
|
|
void RWindow::DestroyOSWindowHandle() {
|
|
// Turn key repeat back on.
|
|
XAutoRepeatOn(platform->display);
|
|
XFlush(platform->display);
|
|
|
|
Logger::Debug(std::format("Destroying sub-windows for window '{}'", this->title));
|
|
XDestroySubwindows(platform->display, platform->window);
|
|
|
|
Logger::Debug(std::format("Destroyed window '{}'", this->title));
|
|
XDestroyWindow(platform->display, platform->window);
|
|
|
|
XCloseDisplay(platform->display);
|
|
delete platform;
|
|
}
|
|
|
|
void RWindow::SetCursorVisible(bool cursor_enable) {
|
|
cursor_visible = cursor_enable;
|
|
if (platform->invisible_cursor == 0) {
|
|
Pixmap blank_pixmap = XCreatePixmap(platform->display, platform->window, 1, 1, 1);
|
|
XColor dummy; dummy.pixel = 0; dummy.red = 0; dummy.flags = 0;
|
|
platform->invisible_cursor = XCreatePixmapCursor(platform->display, blank_pixmap, blank_pixmap, &dummy, &dummy, 0, 0);
|
|
XFreePixmap(platform->display, blank_pixmap);
|
|
}
|
|
|
|
if (!cursor_enable)
|
|
XDefineCursor(platform->display, platform->window, platform->invisible_cursor);
|
|
|
|
if (cursor_enable)
|
|
XUndefineCursor(platform->display, platform->window);
|
|
}
|
|
|
|
void RWindow::DisableResizing() {
|
|
XGetWindowAttributes(platform->display, platform->window, &platform->windowAttributes);
|
|
|
|
platform->hints.flags = PMinSize | PMaxSize;
|
|
platform->hints.min_width = platform->hints.max_width = platform->windowAttributes.width;
|
|
platform->hints.min_height = platform->hints.max_height = platform->windowAttributes.height;
|
|
XSetWMNormalHints(platform->display, platform->window, &platform->hints);
|
|
|
|
this->resizable = false;
|
|
}
|
|
|
|
void RWindow::PollEvents() {
|
|
while(XPending(platform->display)) {
|
|
XNextEvent(platform->display, &platform->xev);
|
|
|
|
if (platform->xev.type == ClientMessage)
|
|
Logger::Info(std::format("Event '{}'", "ClientMessage"));
|
|
|
|
if (platform->xev.xclient.message_type == XInternAtom(platform->display, "WM_PROTOCOLS", False) &&
|
|
static_cast<Atom>(platform->xev.xclient.data.l[0]) == platform->wmDeleteWindow) {
|
|
Close();
|
|
}
|
|
|
|
if (platform->xev.type == FocusIn) {
|
|
Logger::Debug(std::format("Event'{}'", "FocusIn"));
|
|
|
|
if (!key_repeat)
|
|
XAutoRepeatOff(platform->display);
|
|
else
|
|
XAutoRepeatOn(platform->display);
|
|
XFlush(platform->display);
|
|
|
|
|
|
if (cursor_focused) {
|
|
cursor_focused = false;
|
|
SetCursorFocused(true);
|
|
}
|
|
|
|
if (platform->wm_hints) {
|
|
platform->wm_hints->flags &= ~XUrgencyHint;
|
|
XSetWMHints(platform->display, platform->window, platform->wm_hints);
|
|
XFree(platform->wm_hints);
|
|
}
|
|
if (!cursor_visible)
|
|
XDefineCursor(platform->display, platform->window, platform->invisible_cursor);
|
|
|
|
// Get the position of the renderable area relative to the rest of the window.
|
|
XGetWindowAttributes(platform->display, platform->window, &platform->windowAttributes);
|
|
processFocusIn();
|
|
focused = true;
|
|
}
|
|
|
|
if (platform->xev.type == UnmapNotify) {
|
|
if (cursor_focused) {
|
|
SetCursorFocused(false);
|
|
cursor_focused = true;
|
|
}
|
|
}
|
|
|
|
if (platform->xev.type == FocusOut) {
|
|
Logger::Debug(std::format("Event '{}'", "FocusOut"));
|
|
XAutoRepeatOn(platform->display);
|
|
XFlush(platform->display);
|
|
|
|
if (cursor_focused) {
|
|
SetCursorFocused(false);
|
|
cursor_focused = true;
|
|
}
|
|
|
|
if (!cursor_visible)
|
|
XUndefineCursor(platform->display, platform->window);
|
|
|
|
processFocusOut();
|
|
focused = false;
|
|
}
|
|
|
|
if (platform->xev.type == KeyRelease) {
|
|
Logger::Debug(std::format("Event '{}'", "KeyRelease"));
|
|
auto scancode = (X11Scancode) platform->xev.xkey.keycode;
|
|
auto key = GetKeyFromX11Scancode(scancode);
|
|
processKeyRelease(key);
|
|
}
|
|
|
|
if (platform->xev.type == KeyPress) {
|
|
Logger::Debug(std::format("Event '{}'", "KeyPress"));
|
|
auto scancode = (X11Scancode) platform->xev.xkey.keycode;
|
|
auto key = GetKeyFromX11Scancode(scancode);
|
|
processKeyPress(key);
|
|
}
|
|
|
|
if (platform->xev.type == ButtonRelease) {
|
|
|
|
// Mouse Wheel fires both the ButtonPress and ButtonRelease instantaneously.
|
|
// Therefore, we handle it as a specific MouseWheel event rather than a MouseButton event,
|
|
// and only call on ButtonPress, otherwise it will appear to duplicate the mouse wheel scroll.
|
|
if (platform->xev.xbutton.button != 4 && platform->xev.xbutton.button != 5) {
|
|
MouseButton button = GetMouseButtonFromXButton(platform->xev.xbutton.button);
|
|
Logger::Debug(std::format("Event '{}'", "ButtonRelease"));
|
|
processMouseRelease(button);
|
|
}
|
|
}
|
|
|
|
if (platform->xev.type == ButtonPress) {
|
|
|
|
// Mouse Wheel fires both the ButtonPress and ButtonRelease instantaneously.
|
|
// Therefore, we handle it as a specific MouseWheel event rather than a MouseButton event,
|
|
// and only call on ButtonPress, otherwise it will appear to duplicate the mouse wheel scroll.
|
|
if (platform->xev.xbutton.button == 4) {
|
|
processMouseWheel(-1);
|
|
} else if (platform->xev.xbutton.button == 5) {
|
|
processMouseWheel(1);
|
|
} else {
|
|
MouseButton button = GetMouseButtonFromXButton(platform->xev.xbutton.button);
|
|
|
|
Logger::Debug(std::format("Event: MouseButtonPress {}", button.Mnemonic));
|
|
|
|
processMousePress(button);
|
|
}
|
|
}
|
|
|
|
if (platform->xev.type == Expose) {
|
|
Logger::Debug(std::format("Event '{}'", "Expose"));
|
|
}
|
|
|
|
if (platform->xev.type == MotionNotify) {
|
|
|
|
if (toggling_cursor_focus) {
|
|
XWindowAttributes attrs;
|
|
XGetWindowAttributes(platform->display, platform->window, &attrs);
|
|
|
|
if (attrs.map_state == IsViewable) {
|
|
int result = XGrabPointer
|
|
(
|
|
platform->display, platform->window, True,
|
|
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
|
|
GrabModeAsync, GrabModeAsync, platform->window, None, CurrentTime
|
|
);
|
|
|
|
if (result == GrabSuccess) {
|
|
toggling_cursor_focus = false;
|
|
cursor_focused = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (platform->xev.type == ConfigureNotify) {
|
|
if (this->width != platform->xev.xconfigurerequest.width ||
|
|
this->height != platform->xev.xconfigurerequest.height) {
|
|
Logger::Debug(std::format("Event '{}'", "ResizeRequest"));
|
|
|
|
this->width = platform->xev.xconfigurerequest.width;
|
|
this->height = platform->xev.xconfigurerequest.height;
|
|
|
|
auto eventData = WindowResizeRequestEvent();
|
|
eventData.Size = {platform->xev.xconfigurerequest.width, platform->xev.xconfigurerequest.height};
|
|
|
|
OnResizeRequest(eventData);
|
|
OnResizeRequestEvent(eventData);
|
|
}
|
|
//Window Moved.
|
|
if (platform->position.first != platform->xev.xconfigurerequest.x || platform->position.second != platform->xev.xconfigurerequest.y)
|
|
platform->position = { platform->xev.xconfigurerequest.x, platform->xev.xconfigurerequest.y };
|
|
}
|
|
|
|
if (platform->xev.type == VisibilityNotify) {
|
|
if (platform->xev.xvisibility.state == VisibilityFullyObscured)
|
|
platform->window_visible = false;
|
|
else if (platform->xev.xvisibility.state == VisibilityUnobscured ||
|
|
platform->xev.xvisibility.state == VisibilityPartiallyObscured)
|
|
platform->window_visible = true;
|
|
}
|
|
}
|
|
previousKeyboard = currentKeyboard;
|
|
previousMouse.Buttons = currentMouse.Buttons;
|
|
}
|
|
|
|
|
|
// Might make the window go off the screen on some window managers.
|
|
void RWindow::SetSize(int newWidth, int newHeight) {
|
|
if (!resizable)
|
|
return;
|
|
|
|
this->width = newWidth;
|
|
this->height = newHeight;
|
|
XResizeWindow(platform->display, platform->window, newWidth, newHeight);
|
|
XFlush(platform->display);
|
|
Logger::Info(std::format("Set size for '{}' to {} x {}", this->title, newWidth, newHeight));
|
|
}
|
|
|
|
std::pair<int, int> RWindow::GetAccurateCursorCoordinates() const {
|
|
Window root_return, child_return;
|
|
int root_x_ret, root_y_ret;
|
|
int win_x_ret, win_y_ret;
|
|
uint32_t mask_return;
|
|
|
|
// This seems to be relative to the top left corner of the renderable area.
|
|
bool mouseAvailable = XQueryPointer(platform->display, platform->window, &root_return, &child_return, &root_x_ret, &root_y_ret, &win_x_ret, &win_y_ret, &mask_return);
|
|
|
|
if (mouseAvailable) {
|
|
// TODO: normalize coordinates from platform->displaySpace to windowSpace
|
|
// TODO: fire mouse movement event
|
|
std::pair<int, int> m_coords = { win_x_ret, win_y_ret };
|
|
return m_coords;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
bool RWindow::IsVisible() const {
|
|
return platform->window_visible;
|
|
}
|
|
|
|
// TODO: implement integer std::pair<int, int>/3 types
|
|
std::pair<int, int> RWindow::GetPosition() const {
|
|
return platform->position;
|
|
}
|
|
|
|
std::pair<int, int> RWindow::GetSize() const { return { this->width, this->height }; }
|
|
|
|
void RWindow::SetPosition(int x, int y) {
|
|
XMoveWindow(platform->display, platform->window, x, y);
|
|
platform->position = {x, y};
|
|
}
|
|
|
|
void RWindow::SetPosition(const std::pair<int, int>& pos) {
|
|
SetPosition(pos.first, pos.second);
|
|
}
|
|
|
|
void RWindow::Fullscreen() {
|
|
platform->size_before_fullscreen = GetSize();
|
|
|
|
if (!this->resizable) {
|
|
XSizeHints hints;
|
|
hints.flags = PMinSize | PMaxSize;
|
|
hints.min_width = 0;
|
|
hints.min_height = 0;
|
|
hints.max_width = 100000;
|
|
hints.max_height = 100000;
|
|
XSetWMNormalHints(platform->display, platform->window, &hints);
|
|
XFlush(platform->display);
|
|
}
|
|
|
|
Atom wm_state = XInternAtom(platform->display, "_NET_WM_STATE", False);
|
|
Atom wm_fullscreen = XInternAtom(platform->display, "_NET_WM_STATE_FULLSCREEN", False);
|
|
|
|
if (!wm_state || !wm_fullscreen) {
|
|
Logger::Error("We don't have the required atom for fullscreen graphics mode?");
|
|
return;
|
|
}
|
|
|
|
XEvent xev{};
|
|
xev.type = ClientMessage;
|
|
xev.xclient.window = platform->window;
|
|
xev.xclient.message_type = wm_state;
|
|
xev.xclient.format = 32;
|
|
xev.xclient.data.l[0] = 1;
|
|
xev.xclient.data.l[1] = wm_fullscreen;
|
|
xev.xclient.data.l[2] = 0;
|
|
xev.xclient.data.l[3] = 1;
|
|
xev.xclient.data.l[4] = 0;
|
|
|
|
XSendEvent(platform->display,
|
|
DefaultRootWindow(platform->display),
|
|
False,
|
|
SubstructureNotifyMask | SubstructureRedirectMask,
|
|
&xev);
|
|
|
|
XFlush(platform->display);
|
|
fullscreen_mode = true;
|
|
Logger::Debug(std::format("Fullscreened '{}'", this->title));
|
|
}
|
|
|
|
void RWindow::RestoreFromFullscreen() {
|
|
Atom wm_state = XInternAtom(platform->display, "_NET_WM_STATE", False);
|
|
Atom fullscreen = XInternAtom(platform->display, "_NET_WM_STATE_FULLSCREEN", False);
|
|
|
|
if (!wm_state || !fullscreen) {
|
|
Logger::Error("We don't have the required atom for fullscreen graphics mode?");
|
|
return;
|
|
}
|
|
|
|
XEvent xev{};
|
|
xev.type = ClientMessage;
|
|
xev.xclient.window = platform->window;
|
|
xev.xclient.message_type = wm_state;
|
|
xev.xclient.format = 32;
|
|
xev.xclient.data.l[0] = 0;
|
|
xev.xclient.data.l[1] = fullscreen;
|
|
xev.xclient.data.l[2] = 0;
|
|
xev.xclient.data.l[3] = 1;
|
|
xev.xclient.data.l[4] = 0;
|
|
|
|
XSendEvent(
|
|
platform->display,
|
|
DefaultRootWindow(platform->display),
|
|
False,
|
|
SubstructureNotifyMask | SubstructureRedirectMask,
|
|
&xev
|
|
);
|
|
|
|
if (!this->resizable) {
|
|
XSizeHints hints;
|
|
hints.flags = PMinSize | PMaxSize;
|
|
hints.min_width = hints.max_width = platform->size_before_fullscreen.first;
|
|
hints.min_height = hints.max_height = platform->size_before_fullscreen.second;
|
|
XSetWMNormalHints(platform->display, platform->window, &hints);
|
|
|
|
this->width = platform->size_before_fullscreen.first;
|
|
this->height = platform->size_before_fullscreen.second;
|
|
}
|
|
|
|
XFlush(platform->display);
|
|
|
|
fullscreen_mode = false;
|
|
Logger::Debug(std::format("Restored '{}' from Fullscreen", this->title));
|
|
}
|
|
|
|
void RWindow::SetCursorStyle(CursorStyle style) const {
|
|
auto x11_cursor_resolved_enum = (unsigned int) style.X11Cursor;
|
|
Cursor c = XCreateFontCursor(platform->display, x11_cursor_resolved_enum);
|
|
XDefineCursor(platform->display, platform->window, c);
|
|
}
|
|
|
|
void RWindow::SetTitle(const std::string& title) {
|
|
this->title = title;
|
|
XStoreName(platform->display, platform->window, title.c_str());
|
|
}
|
|
|