Compare commits
1 Commits
main
...
b198c673ae
| Author | SHA1 | Date | |
|---|---|---|---|
| b198c673ae |
9
.clangd
9
.clangd
@@ -1,9 +0,0 @@
|
||||
Diagnostics:
|
||||
Suppress: [
|
||||
c99-designator,
|
||||
]
|
||||
|
||||
CompileFlags:
|
||||
Add: [
|
||||
-std=c++20,
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
From c9dd87198ce5262e6ddf6bf3b0c18eb84784d35e Mon Sep 17 00:00:00 2001
|
||||
From 075f6a11926e247ba64efd25a3acf7a3d3be0bbc Mon Sep 17 00:00:00 2001
|
||||
From: Oskari Alaranta <oskari.alaranta@bananymous.com>
|
||||
Date: Wed, 15 Apr 2026 17:52:54 +0300
|
||||
Date: Wed, 15 Apr 2026 16:51:16 +0300
|
||||
Subject: [PATCH] linux-window-server-sdl2
|
||||
|
||||
---
|
||||
@@ -13,8 +13,8 @@ Subject: [PATCH] linux-window-server-sdl2
|
||||
userspace/programs/WindowServer/Window.cpp | 29 +-
|
||||
.../programs/WindowServer/WindowServer.cpp | 47 ++-
|
||||
.../programs/WindowServer/WindowServer.h | 1 +
|
||||
userspace/programs/WindowServer/main.cpp | 357 ++++++++++++------
|
||||
10 files changed, 340 insertions(+), 166 deletions(-)
|
||||
userspace/programs/WindowServer/main.cpp | 362 ++++++++++++------
|
||||
10 files changed, 338 insertions(+), 173 deletions(-)
|
||||
|
||||
diff --git a/userspace/libraries/LibGUI/Widget/Widget.cpp b/userspace/libraries/LibGUI/Widget/Widget.cpp
|
||||
index d6489d87..c532fb04 100644
|
||||
@@ -376,7 +376,7 @@ index 94fbc774..bcd7a6b9 100644
|
||||
private:
|
||||
void on_mouse_move_impl(int32_t new_x, int32_t new_y);
|
||||
diff --git a/userspace/programs/WindowServer/main.cpp b/userspace/programs/WindowServer/main.cpp
|
||||
index 46f2ba6d..520c8e7d 100644
|
||||
index 41bd7e8c..cb066bd3 100644
|
||||
--- a/userspace/programs/WindowServer/main.cpp
|
||||
+++ b/userspace/programs/WindowServer/main.cpp
|
||||
@@ -10,7 +10,6 @@
|
||||
@@ -425,7 +425,7 @@ index 46f2ba6d..520c8e7d 100644
|
||||
signal(sig, exit);
|
||||
for (int sig : non_terminating_signals)
|
||||
signal(sig, SIG_DFL);
|
||||
@@ -208,51 +202,15 @@ int main()
|
||||
@@ -208,55 +202,10 @@ int main()
|
||||
signal(sig, SIG_IGN);
|
||||
|
||||
MUST(LibInput::KeyboardLayout::initialize());
|
||||
@@ -469,18 +469,20 @@ index 46f2ba6d..520c8e7d 100644
|
||||
dprintln("Window server started");
|
||||
|
||||
- if (access("/usr/bin/xbanan", X_OK) == 0)
|
||||
+ if (access("./build/xbanan/xbanan", X_OK) == 0)
|
||||
{
|
||||
if (fork() == 0)
|
||||
{
|
||||
- {
|
||||
- if (fork() == 0)
|
||||
- {
|
||||
- dup2(STDDBG_FILENO, STDOUT_FILENO);
|
||||
- dup2(STDDBG_FILENO, STDERR_FILENO);
|
||||
- execl("/usr/bin/xbanan", "xbanan", NULL);
|
||||
+ execl("./build/xbanan/xbanan", "xbanan", NULL);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -276,26 +234,23 @@ int main()
|
||||
- exit(1);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
auto config = parse_config();
|
||||
|
||||
WindowServer window_server(framebuffer, config.corner_radius);
|
||||
@@ -276,26 +225,23 @@ int main()
|
||||
uint64_t last_sync_us = get_current_us() - sync_interval_us;
|
||||
while (!window_server.is_stopped())
|
||||
{
|
||||
@@ -518,7 +520,7 @@ index 46f2ba6d..520c8e7d 100644
|
||||
if (epoll_events == -1 && errno != EINTR)
|
||||
{
|
||||
dwarnln("epoll_pwait2: {}", strerror(errno));
|
||||
@@ -333,48 +288,6 @@ int main()
|
||||
@@ -333,48 +279,6 @@ int main()
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -567,7 +569,7 @@ index 46f2ba6d..520c8e7d 100644
|
||||
const int client_fd = events[i].data.fd;
|
||||
if (events[i].events & (EPOLLHUP | EPOLLERR))
|
||||
{
|
||||
@@ -500,3 +413,237 @@ int main()
|
||||
@@ -500,3 +404,237 @@ int main()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,14 +26,7 @@ set(CMAKE_CXX_STANDARD 20)
|
||||
add_compile_definitions(-Dstddbg=stdout)
|
||||
add_compile_options(-g)
|
||||
|
||||
set(PLATFORM "banan-os" CACHE STRING "target platform")
|
||||
|
||||
set(VALID_PLATFORMS "banan-os" "SDL3")
|
||||
if(NOT PLATFORM IN_LIST VALID_PLATFORMS)
|
||||
message(FATAL_ERROR "platform \"${PLATFORM}\" not known, valid platforms are ${VALID_PLATFORMS}")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "banan-os")
|
||||
if(BANAN_OS)
|
||||
find_library(BAN ban REQUIRED)
|
||||
add_library(ban SHARED IMPORTED)
|
||||
set_target_properties(ban PROPERTIES IMPORTED_LOCATION "${BAN}")
|
||||
@@ -55,19 +48,16 @@ else()
|
||||
set_target_properties(libc PROPERTIES IMPORTED_LOCATION "${LIBC}")
|
||||
|
||||
add_subdirectory(banan-os/BAN)
|
||||
add_subdirectory(banan-os/userspace/libraries/LibClipboard)
|
||||
add_subdirectory(banan-os/userspace/libraries/LibDEFLATE)
|
||||
add_subdirectory(banan-os/userspace/libraries/LibFont)
|
||||
add_subdirectory(banan-os/userspace/libraries/LibGUI)
|
||||
add_subdirectory(banan-os/userspace/libraries/LibImage)
|
||||
add_subdirectory(banan-os/userspace/libraries/LibInput)
|
||||
|
||||
if(PLATFORM STREQUAL "banan-os")
|
||||
add_subdirectory(banan-os/userspace/libraries/LibClipboard)
|
||||
add_subdirectory(banan-os/userspace/libraries/LibFont)
|
||||
add_subdirectory(banan-os/userspace/libraries/LibGUI)
|
||||
add_subdirectory(banan-os/userspace/libraries/LibImage)
|
||||
add_subdirectory(banan-os/userspace/libraries/LibInput)
|
||||
|
||||
add_subdirectory(banan-os/userspace/programs/ProgramLauncher)
|
||||
add_subdirectory(banan-os/userspace/programs/Terminal)
|
||||
add_subdirectory(banan-os/userspace/programs/WindowServer)
|
||||
endif()
|
||||
add_subdirectory(banan-os/userspace/programs/ProgramLauncher)
|
||||
add_subdirectory(banan-os/userspace/programs/Terminal)
|
||||
add_subdirectory(banan-os/userspace/programs/WindowServer)
|
||||
endif()
|
||||
|
||||
add_subdirectory(xbanan)
|
||||
|
||||
24
LICENSE
24
LICENSE
@@ -1,24 +0,0 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2026, Oskari Alaranta
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
45
README.md
45
README.md
@@ -1,45 +0,0 @@
|
||||
# xbanan
|
||||
|
||||
A portable X11 compatibility layer. There are 2 officially supported platforms: [banan-os's](https://git.bananymous.com/Bananymous/banan-os) native GUI windowing system and SDL3.
|
||||
|
||||
## Running on linux
|
||||
|
||||
### Prerequisites
|
||||
|
||||
You need to have SDL3 development library and X11 headers installed on your system.
|
||||
|
||||
### Building
|
||||
```sh
|
||||
git clone --recursive https://git.bananymous.com/Bananymous/xbanan.git
|
||||
cd xbanan
|
||||
|
||||
cmake -B build -S . -DPLATFORM=SDL3
|
||||
cmake --build build --parallel $(nproc)
|
||||
```
|
||||
|
||||
### Running
|
||||
|
||||
To start xbanan, run the following command in project root
|
||||
```sh
|
||||
./build/xbanan/xbanan
|
||||
```
|
||||
To run X11 apps, specify `DISPLAY=:69` environment variable. For example
|
||||
```sh
|
||||
DISPLAY=:69 xeyes
|
||||
```
|
||||
|
||||
## Porting to a new platform
|
||||
|
||||
Add your platform to the `VALID_PLATFORMS` list in the root `CMakeLists.txt` file. Create a directory for your platform in `xbanan/<your platform>` and define the needed source files and libraries in `xbanan/CMakeLists.txt` based on your platform.
|
||||
|
||||
### Barebones
|
||||
|
||||
For the simplest port, you need to implement 3 functions: `initialize`, `create_window` and `invalidate`. Define the variable `g_platform_ops` declared in `xbanan/Platform.h` with function pointers to the your functions. You can look at the existing platforms and the `xbanan/Platform.h` header for help.
|
||||
|
||||
### Events
|
||||
|
||||
To handle events, you have to hook an event receiver fd for your platform using `register_event_fd` declared in `xbanan/Events.h` and define the `poll_events` function in `g_platform_ops`. When the registered fd becomes readable, xbanan will call your platform's `poll_events` function. This function should drain all of the pending events and call appropriate `on_*_event` function declared in the events header. If your platform does not expose fd(s) that can be polled for events, you can do what the SDL3 port does and create an eventfd/pipe that gets signaled periodically.
|
||||
|
||||
### Installation
|
||||
|
||||
To install xbanan to sysroot, just copy the `build/xbanan/xbanan` to the destination. xbanan is statically linked against the libraries from this repository so you don't have to care about those. **DO NOT** use the cmake's install target, as that installs banan-os libraries to the root of the sysroot. You also need to have the x11 misc fonts at `FONT_PATH/misc` in your sysroot if you want server side font support. The fonts are shipped (public domain) with xbanan, so you can just copy `fonts/misc` into `FONT_PATH`. You can specify the `FONT_PATH` with cmake using `-DFONT_PATH=...` when configuring.
|
||||
1435
xbanan/Base.cpp
1435
xbanan/Base.cpp
File diff suppressed because it is too large
Load Diff
@@ -6,21 +6,3 @@ BAN::ErrorOr<void> setup_client_conneciton(Client& client_info, const xConnClien
|
||||
BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet);
|
||||
|
||||
void invalidate_window(WINDOW wid, int32_t x, int32_t y, int32_t w, int32_t h);
|
||||
|
||||
BAN::ErrorOr<void> send_configure_notify(WINDOW wid);
|
||||
BAN::ErrorOr<void> send_exposure_events_recursive(WINDOW wid);
|
||||
|
||||
void update_cursor(WINDOW wid, int32_t x, int32_t y);
|
||||
|
||||
BAN::ErrorOr<void> destroy_window(Client& client_info, WINDOW wid);
|
||||
|
||||
WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y);
|
||||
|
||||
xPoint get_window_position(WINDOW wid);
|
||||
|
||||
// random number with alpha 0 :D
|
||||
static constexpr uint32_t COLOR_INVISIBLE = 0x00b205AF;
|
||||
|
||||
extern CARD16 g_keymask;
|
||||
extern CARD16 g_butmask;
|
||||
extern WINDOW g_focus_window;
|
||||
|
||||
@@ -1,49 +1,37 @@
|
||||
set(XBANAN_SOURCES
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
Base.cpp
|
||||
Drawing.cpp
|
||||
Events.cpp
|
||||
Extensions.cpp
|
||||
ExtBigReg.cpp
|
||||
ExtRANDR.cpp
|
||||
ExtXRes.cpp
|
||||
Font.cpp
|
||||
Image.cpp
|
||||
Keymap.cpp
|
||||
SafeGetters.cpp
|
||||
)
|
||||
|
||||
set(FONT_PATH ${CMAKE_SOURCE_DIR}/fonts CACHE STRING "path to X11 fonts")
|
||||
|
||||
option(ENABLE_GLX "enable glx extension" ON)
|
||||
|
||||
include(CheckSymbolExists)
|
||||
include(CMakeDependentOption)
|
||||
check_symbol_exists(shmat "sys/shm.h" HAVE_SHMAT)
|
||||
check_symbol_exists(shmdt "sys/shm.h" HAVE_SHMDT)
|
||||
cmake_dependent_option(ENABLE_SHM "enable shm extension" ON "HAVE_SHMAT;HAVE_SHMDT" OFF)
|
||||
|
||||
if(ENABLE_GLX)
|
||||
list(APPEND XBANAN_SOURCES ExtGLX.cpp)
|
||||
set(SOURCES ${SOURCES} ExtGLX.cpp)
|
||||
endif()
|
||||
|
||||
option(ENABLE_SHM "enable shm extension" ON)
|
||||
if(ENABLE_SHM)
|
||||
list(APPEND XBANAN_SOURCES ExtSHM.cpp)
|
||||
set(SOURCES ${SOURCES} ExtSHM.cpp)
|
||||
endif()
|
||||
|
||||
if(PLATFORM STREQUAL "banan-os")
|
||||
list(APPEND XBANAN_SOURCES banan-os/banan-os.cpp)
|
||||
elseif(PLATFORM STREQUAL "SDL3")
|
||||
list(APPEND XBANAN_SOURCES SDL3/sdl3.cpp)
|
||||
endif()
|
||||
|
||||
add_executable(xbanan ${XBANAN_SOURCES})
|
||||
add_executable(xbanan ${SOURCES})
|
||||
banan_link_library(xbanan ban)
|
||||
banan_link_library(xbanan libdeflate)
|
||||
|
||||
target_compile_definitions(xbanan PRIVATE FONT_PATH="${FONT_PATH}")
|
||||
|
||||
if(PLATFORM STREQUAL "banan-os")
|
||||
banan_link_library(xbanan libgui)
|
||||
banan_link_library(xbanan libinput)
|
||||
elseif(PLATFORM STREQUAL "SDL3")
|
||||
find_package(SDL3 REQUIRED)
|
||||
banan_link_library(xbanan SDL3::SDL3)
|
||||
endif()
|
||||
banan_link_library(xbanan libgui)
|
||||
banan_link_library(xbanan libinput)
|
||||
|
||||
target_compile_options(xbanan PRIVATE -Wall -Wextra)
|
||||
target_compile_options(xbanan PRIVATE
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Font.h"
|
||||
#include "Platform.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include <BAN/Vector.h>
|
||||
#include <BAN/UniqPtr.h>
|
||||
#include <BAN/HashMap.h>
|
||||
#include <BAN/HashSet.h>
|
||||
#include <BAN/String.h>
|
||||
#include <BAN/UniqPtr.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
#include <X11/Xproto.h>
|
||||
|
||||
@@ -19,6 +18,15 @@
|
||||
#define dprintln(...)
|
||||
#endif
|
||||
|
||||
typedef CARD32 ATOM;
|
||||
typedef CARD32 BITGRAVITY;
|
||||
typedef CARD32 COLORMAP;
|
||||
typedef CARD32 CURSOR;
|
||||
typedef CARD32 PIXMAP;
|
||||
typedef CARD32 VISUALID;
|
||||
typedef CARD32 WINDOW;
|
||||
typedef CARD32 WINGRAVITY;
|
||||
|
||||
struct Property
|
||||
{
|
||||
CARD8 format;
|
||||
@@ -41,15 +49,20 @@ struct Object
|
||||
|
||||
Type type;
|
||||
|
||||
struct Cursor
|
||||
{
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
int32_t origin_x;
|
||||
int32_t origin_y;
|
||||
BAN::Vector<uint32_t> pixels;
|
||||
};
|
||||
|
||||
struct Window
|
||||
{
|
||||
Client& owner;
|
||||
|
||||
bool mapped { false };
|
||||
bool focused { false };
|
||||
bool fullscreen { false };
|
||||
bool hovered { false };
|
||||
|
||||
uint8_t depth { 0 };
|
||||
int32_t x { 0 };
|
||||
int32_t y { 0 };
|
||||
@@ -59,22 +72,33 @@ struct Object
|
||||
CURSOR cursor;
|
||||
CARD16 c_class;
|
||||
BAN::Vector<WINDOW> children;
|
||||
|
||||
uint32_t width { 0 };
|
||||
uint32_t height { 0 };
|
||||
BAN::Vector<uint32_t> pixels;
|
||||
|
||||
uint32_t background { 0 };
|
||||
|
||||
bool platform_window_invalidated { false };
|
||||
BAN::UniqPtr<PlatformWindow> platform_window;
|
||||
|
||||
BAN::Vector<uint32_t> double_buffer;
|
||||
BAN::Variant<
|
||||
BAN::UniqPtr<LibGUI::Window>,
|
||||
LibGUI::Texture
|
||||
> window;
|
||||
|
||||
BAN::HashMap<Client*, uint32_t> event_masks;
|
||||
|
||||
BAN::HashMap<ATOM, Property> properties;
|
||||
|
||||
LibGUI::Texture& texture()
|
||||
{
|
||||
if (window.has<LibGUI::Texture>())
|
||||
return window.get<LibGUI::Texture>();
|
||||
if (window.has<BAN::UniqPtr<LibGUI::Window>>())
|
||||
return window.get<BAN::UniqPtr<LibGUI::Window>>()->texture();
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
const LibGUI::Texture& texture() const
|
||||
{
|
||||
if (window.has<LibGUI::Texture>())
|
||||
return window.get<LibGUI::Texture>();
|
||||
if (window.has<BAN::UniqPtr<LibGUI::Window>>())
|
||||
return window.get<BAN::UniqPtr<LibGUI::Window>>()->texture();
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
uint32_t full_event_mask() const;
|
||||
BAN::ErrorOr<void> send_event(xEvent event, uint32_t event_mask);
|
||||
};
|
||||
@@ -84,7 +108,8 @@ struct Object
|
||||
CARD8 depth;
|
||||
CARD32 width;
|
||||
CARD32 height;
|
||||
BAN::Vector<uint8_t> data;
|
||||
BAN::ByteSpan data;
|
||||
BAN::Vector<uint8_t> owned_data;
|
||||
};
|
||||
|
||||
struct GraphicsContext
|
||||
@@ -131,7 +156,7 @@ struct Object
|
||||
void (*destructor)(Extension&);
|
||||
};
|
||||
|
||||
BAN::Variant<Window, Pixmap, GraphicsContext, BAN::RefPtr<PCFFont>, BAN::UniqPtr<PlatformCursor>, Extension> object;
|
||||
BAN::Variant<Cursor, Window, Pixmap, GraphicsContext, BAN::RefPtr<PCFFont>, Extension> object;
|
||||
};
|
||||
|
||||
struct Client
|
||||
@@ -146,7 +171,6 @@ struct Client
|
||||
bool has_epollout { false };
|
||||
bool has_bigrequests { false };
|
||||
CARD16 sequence { 0 };
|
||||
BAN::Optional<uint32_t> pid;
|
||||
BAN::Vector<uint8_t> input_buffer;
|
||||
BAN::Vector<uint8_t> output_buffer;
|
||||
BAN::HashSet<CARD32> objects;
|
||||
@@ -157,23 +181,17 @@ struct EpollThingy
|
||||
enum class Type
|
||||
{
|
||||
Client,
|
||||
Event,
|
||||
Window,
|
||||
};
|
||||
|
||||
Type type;
|
||||
BAN::Variant<Client, void*> value;
|
||||
};
|
||||
|
||||
struct DisplayInfo
|
||||
{
|
||||
int32_t x, y;
|
||||
uint32_t w, h;
|
||||
BAN::Variant<Client, LibGUI::Window*> value;
|
||||
};
|
||||
|
||||
extern const xPixmapFormat g_formats[6];
|
||||
extern const xWindowRoot g_root;
|
||||
extern const xDepth g_depth;
|
||||
extern const xVisualType g_visual;
|
||||
extern xWindowRoot g_root;
|
||||
|
||||
extern BAN::HashMap<CARD32, BAN::UniqPtr<Object>> g_objects;
|
||||
|
||||
@@ -185,7 +203,3 @@ extern int g_epoll_fd;
|
||||
extern BAN::HashMap<int, EpollThingy> g_epoll_thingies;
|
||||
|
||||
extern int g_server_grabber_fd;
|
||||
|
||||
extern BAN::Vector<DisplayInfo> g_displays;
|
||||
|
||||
extern CARD32 g_next_global_id;
|
||||
|
||||
@@ -107,7 +107,7 @@ BAN::ErrorOr<void> poly_line(Client& client_info, BAN::ConstByteSpan packet)
|
||||
auto request = decode<xPolyLineReq>(packet).value();
|
||||
|
||||
dprintln("PolyLine");
|
||||
dprintln(" drawable: {h}", request.drawable);
|
||||
dprintln(" drawable: {}", request.drawable);
|
||||
dprintln(" gc: {}", request.gc);
|
||||
dprintln(" coordMode: {}", request.coordMode);
|
||||
|
||||
@@ -161,7 +161,7 @@ BAN::ErrorOr<void> poly_segment(Client& client_info, BAN::ConstByteSpan packet)
|
||||
auto request = decode<xPolySegmentReq>(packet).value();
|
||||
|
||||
dprintln("PolySegment");
|
||||
dprintln(" drawable: {h}", request.drawable);
|
||||
dprintln(" drawable: {}", request.drawable);
|
||||
dprintln(" gc: {}", request.gc);
|
||||
|
||||
auto [out_data_u32, out_w, out_h, _] = TRY(get_drawable_info(client_info, request.drawable, X_PolySegment));
|
||||
@@ -203,7 +203,7 @@ BAN::ErrorOr<void> fill_poly(Client& client_info, BAN::ConstByteSpan packet)
|
||||
auto request = decode<xFillPolyReq>(packet).value();
|
||||
|
||||
dprintln("FillPoly");
|
||||
dprintln(" drawable: {h}", request.drawable);
|
||||
dprintln(" drawable: {}", request.drawable);
|
||||
dprintln(" gc: {}", request.gc);
|
||||
dprintln(" shape: {}", request.shape);
|
||||
dprintln(" coordMode: {}", request.coordMode);
|
||||
@@ -278,7 +278,7 @@ BAN::ErrorOr<void> poly_fill_rectangle(Client& client_info, BAN::ConstByteSpan p
|
||||
auto request = decode<xPolyFillRectangleReq>(packet).value();
|
||||
|
||||
dprintln("PolyFillRectangle");
|
||||
dprintln(" drawable: {h}", request.drawable);
|
||||
dprintln(" drawable: {}", request.drawable);
|
||||
dprintln(" gc: {}", request.gc);
|
||||
|
||||
auto [out_data_u32, out_w, out_h, _] = TRY(get_drawable_info(client_info, request.drawable, X_PolyFillRectangle));
|
||||
@@ -300,9 +300,6 @@ BAN::ErrorOr<void> poly_fill_rectangle(Client& client_info, BAN::ConstByteSpan p
|
||||
const int32_t max_x = BAN::Math::min<int32_t>(rect.x + rect.width, out_w);
|
||||
const int32_t max_y = BAN::Math::min<int32_t>(rect.y + rect.height, out_h);
|
||||
|
||||
if (min_x >= max_x || min_y >= max_y)
|
||||
continue;
|
||||
|
||||
for (int32_t y = min_y; y < max_y; y++)
|
||||
for (int32_t x = min_x; x < max_x; x++)
|
||||
if (!gc.is_clipped(x, y))
|
||||
@@ -320,7 +317,7 @@ BAN::ErrorOr<void> poly_fill_arc(Client& client_info, BAN::ConstByteSpan packet)
|
||||
auto request = decode<xPolyFillArcReq>(packet).value();
|
||||
|
||||
dprintln("PolyFillArc");
|
||||
dprintln(" drawable: {h}", request.drawable);
|
||||
dprintln(" drawable: {}", request.drawable);
|
||||
dprintln(" gc: {}", request.gc);
|
||||
|
||||
auto [out_data_32, out_w, out_h, _] = TRY(get_drawable_info(client_info, request.drawable, X_PolyFillArc));
|
||||
@@ -354,9 +351,6 @@ BAN::ErrorOr<void> poly_fill_arc(Client& client_info, BAN::ConstByteSpan packet)
|
||||
const int32_t max_x = BAN::Math::min<int32_t>(out_w, arc.x + arc.width);
|
||||
const int32_t max_y = BAN::Math::min<int32_t>(out_h, arc.y + arc.height);
|
||||
|
||||
if (min_x >= max_x || min_y >= max_y)
|
||||
continue;
|
||||
|
||||
const auto rx = arc.width / 2;
|
||||
const auto ry = arc.height / 2;
|
||||
|
||||
|
||||
@@ -1,559 +0,0 @@
|
||||
#include "Base.h"
|
||||
#include "Definitions.h"
|
||||
#include "Keymap.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
void register_event_fd(int fd, void* data)
|
||||
{
|
||||
epoll_event event { .events = EPOLLIN, .data = { .fd = fd } };
|
||||
epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &event);
|
||||
|
||||
MUST(g_epoll_thingies.insert(fd, {
|
||||
.type = EpollThingy::Type::Event,
|
||||
.value = data,
|
||||
}));
|
||||
}
|
||||
|
||||
void unregister_event_fd(int fd)
|
||||
{
|
||||
epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
|
||||
g_epoll_thingies.remove(fd);
|
||||
}
|
||||
|
||||
void on_window_close_event(WINDOW wid)
|
||||
{
|
||||
static CARD32 WM_PROTOCOLS = g_atoms_name_to_id["WM_PROTOCOLS"_sv];
|
||||
static CARD32 WM_DELETE_WINDOW = g_atoms_name_to_id["WM_DELETE_WINDOW"_sv];
|
||||
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
|
||||
const bool supports_wm_delete_winow =
|
||||
[&window]
|
||||
{
|
||||
auto wm_protocols_it = window.properties.find(WM_PROTOCOLS);
|
||||
if (wm_protocols_it == window.properties.end())
|
||||
return false;
|
||||
|
||||
const auto& wm_protocols = wm_protocols_it->value;
|
||||
if (wm_protocols.type != XA_ATOM || wm_protocols.format != 32)
|
||||
return false;
|
||||
|
||||
const auto atoms = BAN::ConstByteSpan(wm_protocols.data.span()).as_span<const CARD32>();
|
||||
for (auto atom : atoms)
|
||||
if (atom == WM_DELETE_WINDOW)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (!supports_wm_delete_winow)
|
||||
MUST(destroy_window(window.owner, wid));
|
||||
else
|
||||
{
|
||||
xEvent event { .u = {
|
||||
.clientMessage = {
|
||||
.window = wid,
|
||||
.u = { .l = {
|
||||
.type = WM_PROTOCOLS,
|
||||
.longs0 = static_cast<INT32>(WM_DELETE_WINDOW),
|
||||
.longs1 = static_cast<INT32>(time(nullptr)),
|
||||
}}
|
||||
}
|
||||
}};
|
||||
event.u.u.type = ClientMessage;
|
||||
event.u.u.detail = 32;
|
||||
event.u.u.sequenceNumber = window.owner.sequence;
|
||||
MUST(encode(window.owner.output_buffer, event));
|
||||
}
|
||||
}
|
||||
|
||||
void on_window_resize_event(WINDOW wid, uint32_t new_width, uint32_t new_height)
|
||||
{
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
|
||||
window.width = new_width;
|
||||
window.height = new_height;
|
||||
|
||||
MUST(window.pixels.resize(new_width * new_height));
|
||||
for (auto& pixel : window.pixels)
|
||||
pixel = window.background;
|
||||
|
||||
MUST(window.double_buffer.resize(new_width * new_height));
|
||||
for (auto& pixel : window.double_buffer)
|
||||
pixel = window.background;
|
||||
|
||||
MUST(send_configure_notify(wid));
|
||||
|
||||
MUST(send_exposure_events_recursive(wid));
|
||||
}
|
||||
|
||||
void on_window_move_event(WINDOW wid, int32_t x, int32_t y)
|
||||
{
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
|
||||
window.x = x;
|
||||
window.y = y;
|
||||
|
||||
MUST(send_configure_notify(wid));
|
||||
}
|
||||
|
||||
void on_window_focus_event(WINDOW wid, bool focused)
|
||||
{
|
||||
if (focused)
|
||||
g_focus_window = wid;
|
||||
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
if (window.focused == focused)
|
||||
return;
|
||||
|
||||
window.focused = focused;
|
||||
|
||||
// FIXME: handle children
|
||||
|
||||
xEvent event { .u = {
|
||||
.focus = {
|
||||
.window = wid,
|
||||
.mode = NotifyNormal,
|
||||
}
|
||||
}};
|
||||
event.u.u.type = focused ? FocusIn : FocusOut,
|
||||
event.u.u.detail = NotifyNonlinear;
|
||||
MUST(window.send_event(event, FocusChangeMask));
|
||||
}
|
||||
|
||||
void on_window_fullscreen_event(WINDOW wid, bool is_fullscreen)
|
||||
{
|
||||
static CARD32 _NET_WM_STATE = g_atoms_name_to_id["_NET_WM_STATE"_sv];
|
||||
static CARD32 _NET_WM_STATE_FULLSCREEN = g_atoms_name_to_id["_NET_WM_STATE_FULLSCREEN"_sv];
|
||||
|
||||
auto window_it = g_objects.find(wid);
|
||||
if (window_it == g_objects.end() || window_it->value->type != Object::Type::Window)
|
||||
return;
|
||||
|
||||
auto& window = window_it->value->object.get<Object::Window>();
|
||||
|
||||
window.fullscreen = is_fullscreen;
|
||||
|
||||
auto it = window.properties.find(_NET_WM_STATE);
|
||||
if (it == window.properties.end())
|
||||
it = MUST(window.properties.emplace(_NET_WM_STATE));
|
||||
|
||||
auto& _net_wm_state = it->value;
|
||||
if (_net_wm_state.type != XA_ATOM || _net_wm_state.format != 32)
|
||||
_net_wm_state = {};
|
||||
|
||||
_net_wm_state.type = XA_ATOM;
|
||||
_net_wm_state.format = 32;
|
||||
|
||||
for (size_t i = 0; i + 4 <= _net_wm_state.data.size(); i += 4)
|
||||
{
|
||||
const auto atom = *reinterpret_cast<const CARD32*>(_net_wm_state.data.data() + i);
|
||||
if (atom != _NET_WM_STATE_FULLSCREEN)
|
||||
continue;
|
||||
_net_wm_state.data.remove(i);
|
||||
_net_wm_state.data.remove(i);
|
||||
_net_wm_state.data.remove(i);
|
||||
_net_wm_state.data.remove(i);
|
||||
i -= 4;
|
||||
}
|
||||
|
||||
if (is_fullscreen)
|
||||
{
|
||||
const size_t atom_count = _net_wm_state.data.size() / 4;
|
||||
MUST(_net_wm_state.data.resize(_net_wm_state.data.size() + 4));
|
||||
reinterpret_cast<CARD32*>(_net_wm_state.data.data())[atom_count] = _NET_WM_STATE_FULLSCREEN;
|
||||
}
|
||||
|
||||
xEvent event = { .u = {
|
||||
.property = {
|
||||
.window = wid,
|
||||
.atom = _NET_WM_STATE,
|
||||
.time = static_cast<CARD32>(time(nullptr)),
|
||||
.state = PropertyNewValue,
|
||||
}
|
||||
}};
|
||||
event.u.u.type = PropertyNotify;
|
||||
MUST(window.send_event(event, PropertyChangeMask));
|
||||
}
|
||||
|
||||
static void send_key_button_pointer_event(WINDOW root_wid, BYTE detail, uint32_t event_mask, BYTE event_type, KeyButMask state)
|
||||
{
|
||||
int32_t event_x, event_y;
|
||||
|
||||
{
|
||||
auto& object = *g_objects[root_wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
event_x = window.cursor_x;
|
||||
event_y = window.cursor_y;
|
||||
}
|
||||
|
||||
const auto child_wid = find_child_window(root_wid, event_x, event_y);
|
||||
|
||||
auto wid = child_wid;
|
||||
while (wid != None)
|
||||
{
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
if (window.full_event_mask() & event_mask)
|
||||
break;
|
||||
|
||||
event_x += window.x;
|
||||
event_y += window.y;
|
||||
wid = window.parent;
|
||||
}
|
||||
|
||||
if (wid == None)
|
||||
{
|
||||
if (event_type == MotionNotify && g_butmask == 0)
|
||||
return;
|
||||
wid = root_wid;
|
||||
}
|
||||
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
|
||||
const auto [root_x, root_y] = get_window_position(wid);
|
||||
xEvent event { .u = {
|
||||
.keyButtonPointer = {
|
||||
.time = static_cast<CARD32>(time(nullptr)),
|
||||
.root = g_root.windowId,
|
||||
.event = wid,
|
||||
.child = static_cast<CARD32>(child_wid == wid ? None : child_wid),
|
||||
.rootX = static_cast<INT16>(root_x + event_x),
|
||||
.rootY = static_cast<INT16>(root_y + event_y),
|
||||
.eventX = static_cast<INT16>(event_x),
|
||||
.eventY = static_cast<INT16>(event_y),
|
||||
.state = state,
|
||||
.sameScreen = xTrue,
|
||||
}
|
||||
}};
|
||||
event.u.u.type = event_type,
|
||||
event.u.u.detail = detail;
|
||||
MUST(window.send_event(event, event_mask));
|
||||
}
|
||||
|
||||
static void update_cursor_position_recursive(WINDOW wid, int32_t new_x, int32_t new_y)
|
||||
{
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
|
||||
window.cursor_x = new_x;
|
||||
window.cursor_y = new_y;
|
||||
|
||||
for (auto child_wid : window.children)
|
||||
{
|
||||
auto& child_object = *g_objects[child_wid];
|
||||
ASSERT(child_object.type == Object::Type::Window);
|
||||
auto& child_window = child_object.object.get<Object::Window>();
|
||||
update_cursor_position_recursive(child_wid, new_x - child_window.x, new_y - child_window.y);
|
||||
}
|
||||
}
|
||||
|
||||
static BAN::Vector<WINDOW> get_path_to_child(WINDOW wid, int32_t x, int32_t y)
|
||||
{
|
||||
BAN::Vector<WINDOW> result;
|
||||
|
||||
const auto window_contains =
|
||||
[](const Object::Window& window, int32_t x, int32_t y) -> bool
|
||||
{
|
||||
return x >= 0 && y >= 0 && x < window.width && y < window.height;
|
||||
};
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
|
||||
const auto& window = object.object.get<Object::Window>();
|
||||
if (!window_contains(window, x, y))
|
||||
break;
|
||||
|
||||
MUST(result.push_back(wid));
|
||||
|
||||
const WINDOW old_wid = wid;
|
||||
for (auto child_wid : window.children)
|
||||
{
|
||||
const auto& child_object = *g_objects[child_wid];
|
||||
ASSERT(child_object.type == Object::Type::Window);
|
||||
|
||||
const auto& child_window = child_object.object.get<Object::Window>();
|
||||
if (!window_contains(child_window, x - child_window.x, y - child_window.y))
|
||||
continue;
|
||||
wid = child_wid;
|
||||
break;
|
||||
}
|
||||
|
||||
if (old_wid == wid)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void send_enter_and_leave_events(WINDOW old_wid, int32_t old_x, int32_t old_y, WINDOW new_wid, int32_t new_x, int32_t new_y)
|
||||
{
|
||||
// FIXME: correct event_x and event_y values in events
|
||||
|
||||
const auto old_child_path = get_path_to_child(old_wid, old_x, old_y);
|
||||
const auto new_child_path = get_path_to_child(new_wid, new_x, new_y);
|
||||
|
||||
size_t common_ancestors = 0;
|
||||
while (common_ancestors < old_child_path.size() && common_ancestors < new_child_path.size())
|
||||
{
|
||||
if (old_child_path[common_ancestors] != new_child_path[common_ancestors])
|
||||
break;
|
||||
common_ancestors++;
|
||||
}
|
||||
|
||||
if (old_child_path.size() == common_ancestors && new_child_path.size() == common_ancestors)
|
||||
return;
|
||||
|
||||
const bool linear = (common_ancestors == old_child_path.size() || common_ancestors == new_child_path.size());
|
||||
|
||||
const auto get_flags =
|
||||
[](WINDOW wid) -> BYTE
|
||||
{
|
||||
return ELFlagSameScreen | (g_focus_window == wid ? ELFlagFocus : 0);
|
||||
};
|
||||
|
||||
size_t leave_events = old_child_path.size() - common_ancestors;
|
||||
if (linear && common_ancestors && old_child_path.size() < new_child_path.size())
|
||||
leave_events++;
|
||||
|
||||
size_t enter_events = new_child_path.size() - common_ancestors;
|
||||
if (linear && common_ancestors && new_child_path.size() < old_child_path.size())
|
||||
enter_events++;
|
||||
|
||||
for (size_t i = 0; i < leave_events; i++)
|
||||
{
|
||||
const bool first = (i == 0);
|
||||
const BYTE detail =
|
||||
[&]() -> BYTE {
|
||||
if (!linear)
|
||||
return first ? NotifyNonlinear : NotifyNonlinearVirtual;
|
||||
if (!first)
|
||||
return NotifyVirtual;
|
||||
return (old_child_path.size() > new_child_path.size())
|
||||
? NotifyAncestor
|
||||
: NotifyInferior;
|
||||
}();
|
||||
|
||||
const auto& wid = old_child_path[old_child_path.size() - i - 1];
|
||||
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
|
||||
const auto [root_x, root_y] = get_window_position(wid);
|
||||
xEvent event { .u = {
|
||||
.enterLeave = {
|
||||
.time = static_cast<CARD32>(time(nullptr)),
|
||||
.root = g_root.windowId,
|
||||
.event = wid,
|
||||
.child = first ? static_cast<WINDOW>(None) : old_child_path.back(),
|
||||
.rootX = static_cast<INT16>(root_x + old_x),
|
||||
.rootY = static_cast<INT16>(root_y + old_y),
|
||||
.eventX = static_cast<INT16>(old_x),
|
||||
.eventY = static_cast<INT16>(old_y),
|
||||
.state = static_cast<KeyButMask>(g_keymask | g_butmask),
|
||||
.mode = NotifyNormal,
|
||||
.flags = get_flags(wid),
|
||||
}
|
||||
}};
|
||||
event.u.u.type = LeaveNotify,
|
||||
event.u.u.detail = detail;
|
||||
MUST(window.send_event(event, LeaveWindowMask));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < enter_events; i++)
|
||||
{
|
||||
const bool last = (i == enter_events - 1);
|
||||
const BYTE detail =
|
||||
[&]() -> BYTE {
|
||||
if (!linear)
|
||||
return last ? NotifyNonlinear : NotifyNonlinearVirtual;
|
||||
if (!last)
|
||||
return NotifyVirtual;
|
||||
return (old_child_path.size() > new_child_path.size())
|
||||
? NotifyInferior
|
||||
: NotifyAncestor;
|
||||
}();
|
||||
|
||||
const auto& wid = new_child_path[new_child_path.size() - enter_events + i];
|
||||
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
|
||||
const auto [root_x, root_y] = get_window_position(wid);
|
||||
xEvent event { .u = {
|
||||
.enterLeave = {
|
||||
.time = static_cast<CARD32>(time(nullptr)),
|
||||
.root = g_root.windowId,
|
||||
.event = wid,
|
||||
.child = last ? static_cast<WINDOW>(None) : new_child_path.back(),
|
||||
.rootX = static_cast<INT16>(root_x + new_x),
|
||||
.rootY = static_cast<INT16>(root_y + new_y),
|
||||
.eventX = static_cast<INT16>(new_x),
|
||||
.eventY = static_cast<INT16>(new_y),
|
||||
.state = static_cast<KeyButMask>(g_keymask | g_butmask),
|
||||
.mode = NotifyNormal,
|
||||
.flags = get_flags(wid),
|
||||
}
|
||||
}};
|
||||
event.u.u.type = EnterNotify,
|
||||
event.u.u.detail = detail;
|
||||
MUST(window.send_event(event, EnterWindowMask));
|
||||
}
|
||||
|
||||
for (const auto wid : old_child_path)
|
||||
g_objects[wid]->object.get<Object::Window>().hovered = false;
|
||||
for (const auto wid : new_child_path)
|
||||
g_objects[wid]->object.get<Object::Window>().hovered = true;
|
||||
}
|
||||
|
||||
static WINDOW s_current_top_level_window = None;
|
||||
|
||||
void on_window_leave_event(WINDOW wid)
|
||||
{
|
||||
if (s_current_top_level_window != wid)
|
||||
return;
|
||||
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
|
||||
send_enter_and_leave_events(wid, window.cursor_x, window.cursor_y, wid, -1, -1);
|
||||
|
||||
s_current_top_level_window = None;
|
||||
}
|
||||
|
||||
void on_mouse_move_event(WINDOW wid, int32_t x, int32_t y)
|
||||
{
|
||||
auto& object = *g_objects[wid];
|
||||
ASSERT(object.type == Object::Type::Window);
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
|
||||
update_cursor(wid, x, y);
|
||||
|
||||
if (auto it = g_objects.find(s_current_top_level_window); it == g_objects.end())
|
||||
send_enter_and_leave_events(wid, -1, -1, wid, x, y);
|
||||
else
|
||||
{
|
||||
ASSERT(it->value->type == Object::Type::Window);
|
||||
auto& old_window = it->value->object.get<Object::Window>();
|
||||
send_enter_and_leave_events(s_current_top_level_window, old_window.cursor_x, old_window.cursor_y, wid, x, y);
|
||||
}
|
||||
|
||||
s_current_top_level_window = wid;
|
||||
|
||||
update_cursor_position_recursive(wid, x, y);
|
||||
|
||||
// TODO: optimize with PointerMotionHint
|
||||
uint32_t event_mask = PointerMotionMask | PointerMotionHintMask;
|
||||
if (g_butmask) event_mask |= ButtonMotionMask;
|
||||
if (g_butmask & Button1Mask) event_mask |= Button1MotionMask;
|
||||
if (g_butmask & Button2Mask) event_mask |= Button2MotionMask;
|
||||
if (g_butmask & Button3Mask) event_mask |= Button3MotionMask;
|
||||
if (g_butmask & Button4Mask) event_mask |= Button4MotionMask;
|
||||
if (g_butmask & Button5Mask) event_mask |= Button5MotionMask;
|
||||
send_key_button_pointer_event(wid, NotifyNormal, event_mask, MotionNotify, g_keymask | g_butmask);
|
||||
}
|
||||
|
||||
void on_mouse_button_event(WINDOW wid, uint8_t xbutton, bool pressed)
|
||||
{
|
||||
uint16_t mask = 0;
|
||||
switch (xbutton)
|
||||
{
|
||||
case Button1: mask = Button1Mask; break;
|
||||
case Button2: mask = Button2Mask; break;
|
||||
case Button3: mask = Button3Mask; break;
|
||||
case Button4: mask = Button4Mask; break;
|
||||
case Button5: mask = Button5Mask; break;
|
||||
}
|
||||
|
||||
const auto old_state = g_keymask | g_butmask;
|
||||
|
||||
if (pressed)
|
||||
g_butmask |= mask;
|
||||
else
|
||||
g_butmask &= ~mask;
|
||||
|
||||
send_key_button_pointer_event(
|
||||
wid,
|
||||
xbutton,
|
||||
pressed ? ButtonPressMask : ButtonReleaseMask,
|
||||
pressed ? ButtonPress : ButtonRelease,
|
||||
old_state
|
||||
);
|
||||
}
|
||||
|
||||
void on_key_event(WINDOW wid, uint8_t scancode, uint8_t xmod, bool pressed)
|
||||
{
|
||||
const uint8_t xkeycode = scancode + g_keymap_min_keycode;
|
||||
if (xkeycode < g_keymap_min_keycode || xkeycode > g_keymap_max_keycode)
|
||||
return;
|
||||
|
||||
{
|
||||
const uint8_t byte = xkeycode / 8;
|
||||
const uint8_t mask = 1 << (xkeycode % 8);
|
||||
if (pressed)
|
||||
g_pressed_keys[byte] |= mask;
|
||||
else
|
||||
g_pressed_keys[byte] &= ~mask;
|
||||
}
|
||||
|
||||
const auto old_state = g_keymask | g_butmask;
|
||||
|
||||
g_keymask = xmod;
|
||||
|
||||
send_key_button_pointer_event(
|
||||
wid,
|
||||
xkeycode,
|
||||
pressed ? KeyPressMask : KeyReleaseMask,
|
||||
pressed ? KeyPress : KeyRelease,
|
||||
old_state
|
||||
);
|
||||
}
|
||||
|
||||
void on_keymap_changed()
|
||||
{
|
||||
for (auto& [id, object] : g_objects)
|
||||
{
|
||||
if (object->type != Object::Type::Window)
|
||||
continue;
|
||||
|
||||
auto& client_info = object->object.get<Object::Window>().owner;
|
||||
|
||||
xEvent event {};
|
||||
event.u.u.type = MappingNotify;
|
||||
event.u.u.sequenceNumber = client_info.sequence;
|
||||
|
||||
event.u.mappingNotify.request = MappingKeyboard,
|
||||
event.u.mappingNotify.firstKeyCode = g_keymap_min_keycode,
|
||||
event.u.mappingNotify.count = g_keymap_size,
|
||||
MUST(encode(client_info.output_buffer, event));
|
||||
|
||||
event.u.mappingNotify.request = MappingModifier;
|
||||
MUST(encode(client_info.output_buffer, event));
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
void register_event_fd(int fd, void* data);
|
||||
void unregister_event_fd(int fd);
|
||||
|
||||
void on_window_close_event(WINDOW wid);
|
||||
void on_window_resize_event(WINDOW wid, uint32_t width, uint32_t height);
|
||||
void on_window_move_event(WINDOW wid, int32_t x, int32_t y);
|
||||
void on_window_focus_event(WINDOW wid, bool focused);
|
||||
void on_window_fullscreen_event(WINDOW wid, bool is_fullscreen);
|
||||
void on_window_leave_event(WINDOW wid);
|
||||
|
||||
void on_mouse_move_event(WINDOW wid, int32_t x, int32_t y);
|
||||
void on_mouse_button_event(WINDOW wid, uint8_t xbutton, bool pressed);
|
||||
void on_key_event(WINDOW wid, uint8_t scancode, uint8_t xmod, bool pressed);
|
||||
|
||||
void on_keymap_changed();
|
||||
@@ -1,63 +1,63 @@
|
||||
#include "Extensions.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <GL/glx.h>
|
||||
#include <GL/glxproto.h>
|
||||
#include <GL/glxtokens.h>
|
||||
|
||||
using BOOL32 = CARD32;
|
||||
|
||||
CARD32 g_fb_configs[2][24][2] {
|
||||
{
|
||||
{ GLX_FBCONFIG_ID, 1 },
|
||||
{ GLX_VISUAL_ID, g_visual.visualID },
|
||||
{ GLX_BUFFER_SIZE, 32 },
|
||||
{ GLX_LEVEL, 0 },
|
||||
{ GLX_DOUBLEBUFFER, xTrue },
|
||||
{ GLX_STEREO, xFalse },
|
||||
{ GLX_RENDER_TYPE, GLX_RGBA_BIT },
|
||||
{ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT },
|
||||
{ GLX_X_RENDERABLE, xTrue },
|
||||
{ GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR },
|
||||
{ GLX_CONFIG_CAVEAT, GLX_NONE },
|
||||
{ GLX_TRANSPARENT_TYPE, GLX_NONE },
|
||||
{ GLX_RED_SIZE, 8 },
|
||||
{ GLX_GREEN_SIZE, 8 },
|
||||
{ GLX_BLUE_SIZE, 8 },
|
||||
{ GLX_ALPHA_SIZE, 8 },
|
||||
{ GLX_DEPTH_SIZE, 24 },
|
||||
{ GLX_STENCIL_SIZE, 8 },
|
||||
{ GLX_ACCUM_RED_SIZE, 0 },
|
||||
{ GLX_ACCUM_GREEN_SIZE, 0 },
|
||||
{ GLX_ACCUM_BLUE_SIZE, 0 },
|
||||
{ GLX_ACCUM_ALPHA_SIZE, 0 },
|
||||
{ GLX_SAMPLE_BUFFERS_SGIS, 0 },
|
||||
{ GLX_SAMPLES_SGIS, 0 },
|
||||
{ GLX_FBCONFIG_ID, 1 },
|
||||
{ GLX_VISUAL_ID, g_visual.visualID },
|
||||
{ GLX_BUFFER_SIZE, 32 },
|
||||
{ GLX_LEVEL, 0 },
|
||||
{ GLX_DOUBLEBUFFER, True },
|
||||
{ GLX_STEREO, False },
|
||||
{ GLX_RENDER_TYPE, GLX_RGBA_BIT },
|
||||
{ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT },
|
||||
{ GLX_X_RENDERABLE, True },
|
||||
{ GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR },
|
||||
{ GLX_CONFIG_CAVEAT, GLX_NONE },
|
||||
{ GLX_TRANSPARENT_TYPE, GLX_NONE },
|
||||
{ GLX_RED_SIZE, 8 },
|
||||
{ GLX_GREEN_SIZE, 8 },
|
||||
{ GLX_BLUE_SIZE, 8 },
|
||||
{ GLX_ALPHA_SIZE, 8 },
|
||||
{ GLX_DEPTH_SIZE, 24 },
|
||||
{ GLX_STENCIL_SIZE, 8 },
|
||||
{ GLX_ACCUM_RED_SIZE, 0 },
|
||||
{ GLX_ACCUM_GREEN_SIZE, 0 },
|
||||
{ GLX_ACCUM_BLUE_SIZE, 0 },
|
||||
{ GLX_ACCUM_ALPHA_SIZE, 0 },
|
||||
{ GLX_SAMPLE_BUFFERS, 0 },
|
||||
{ GLX_SAMPLES, 0 },
|
||||
},
|
||||
{
|
||||
{ GLX_FBCONFIG_ID, 2 },
|
||||
{ GLX_VISUAL_ID, g_visual.visualID },
|
||||
{ GLX_BUFFER_SIZE, 32 },
|
||||
{ GLX_LEVEL, 0 },
|
||||
{ GLX_DOUBLEBUFFER, xFalse },
|
||||
{ GLX_STEREO, xFalse },
|
||||
{ GLX_RENDER_TYPE, GLX_RGBA_BIT },
|
||||
{ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT },
|
||||
{ GLX_X_RENDERABLE, xTrue },
|
||||
{ GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR },
|
||||
{ GLX_CONFIG_CAVEAT, GLX_NONE },
|
||||
{ GLX_TRANSPARENT_TYPE, GLX_NONE },
|
||||
{ GLX_RED_SIZE, 8 },
|
||||
{ GLX_GREEN_SIZE, 8 },
|
||||
{ GLX_BLUE_SIZE, 8 },
|
||||
{ GLX_ALPHA_SIZE, 8 },
|
||||
{ GLX_DEPTH_SIZE, 24 },
|
||||
{ GLX_STENCIL_SIZE, 8 },
|
||||
{ GLX_ACCUM_RED_SIZE, 0 },
|
||||
{ GLX_ACCUM_GREEN_SIZE, 0 },
|
||||
{ GLX_ACCUM_BLUE_SIZE, 0 },
|
||||
{ GLX_ACCUM_ALPHA_SIZE, 0 },
|
||||
{ GLX_SAMPLE_BUFFERS_SGIS, 0 },
|
||||
{ GLX_SAMPLES_SGIS, 0 },
|
||||
{ GLX_FBCONFIG_ID, 2 },
|
||||
{ GLX_VISUAL_ID, g_visual.visualID },
|
||||
{ GLX_BUFFER_SIZE, 32 },
|
||||
{ GLX_LEVEL, 0 },
|
||||
{ GLX_DOUBLEBUFFER, False },
|
||||
{ GLX_STEREO, False },
|
||||
{ GLX_RENDER_TYPE, GLX_RGBA_BIT },
|
||||
{ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT },
|
||||
{ GLX_X_RENDERABLE, True },
|
||||
{ GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR },
|
||||
{ GLX_CONFIG_CAVEAT, GLX_NONE },
|
||||
{ GLX_TRANSPARENT_TYPE, GLX_NONE },
|
||||
{ GLX_RED_SIZE, 8 },
|
||||
{ GLX_GREEN_SIZE, 8 },
|
||||
{ GLX_BLUE_SIZE, 8 },
|
||||
{ GLX_ALPHA_SIZE, 8 },
|
||||
{ GLX_DEPTH_SIZE, 24 },
|
||||
{ GLX_STENCIL_SIZE, 8 },
|
||||
{ GLX_ACCUM_RED_SIZE, 0 },
|
||||
{ GLX_ACCUM_GREEN_SIZE, 0 },
|
||||
{ GLX_ACCUM_BLUE_SIZE, 0 },
|
||||
{ GLX_ACCUM_ALPHA_SIZE, 0 },
|
||||
{ GLX_SAMPLE_BUFFERS, 0 },
|
||||
{ GLX_SAMPLES, 0 },
|
||||
},
|
||||
};
|
||||
|
||||
@@ -337,7 +337,7 @@ BAN::ErrorOr<void> extension_glx(Client& client_info, BAN::ConstByteSpan packet)
|
||||
|
||||
const auto& object = g_objects[request.drawable];
|
||||
ASSERT(object->type == Object::Type::Window);
|
||||
const auto& window = object->object.get<Object::Window>();
|
||||
const auto& texture = object->object.get<Object::Window>().texture();
|
||||
|
||||
xGLXGetDrawableAttributesReply reply {
|
||||
.type = X_Reply,
|
||||
@@ -348,16 +348,16 @@ BAN::ErrorOr<void> extension_glx(Client& client_info, BAN::ConstByteSpan packet)
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
TRY(encode<CARD32>(client_info.output_buffer, GLX_WIDTH));
|
||||
TRY(encode<CARD32>(client_info.output_buffer, window.width));
|
||||
TRY(encode<CARD32>(client_info.output_buffer, texture.width()));
|
||||
|
||||
TRY(encode<CARD32>(client_info.output_buffer, GLX_HEIGHT));
|
||||
TRY(encode<CARD32>(client_info.output_buffer, window.height));
|
||||
TRY(encode<CARD32>(client_info.output_buffer, texture.height()));
|
||||
|
||||
TRY(encode<CARD32>(client_info.output_buffer, GLX_PRESERVED_CONTENTS));
|
||||
TRY(encode<CARD32>(client_info.output_buffer, xTrue));
|
||||
|
||||
TRY(encode<CARD32>(client_info.output_buffer, GLX_LARGEST_PBUFFER));
|
||||
TRY(encode<CARD32>(client_info.output_buffer, window.width * window.height));
|
||||
TRY(encode<CARD32>(client_info.output_buffer, texture.width() * texture.height()));
|
||||
|
||||
TRY(encode<CARD32>(client_info.output_buffer, GLX_FBCONFIG_ID));
|
||||
TRY(encode<CARD32>(client_info.output_buffer, 1));
|
||||
|
||||
@@ -6,64 +6,12 @@
|
||||
|
||||
#include <time.h>
|
||||
|
||||
struct RANDRDisplay
|
||||
{
|
||||
CARD32 crtc_id;
|
||||
CARD32 output_id;
|
||||
CARD32 mode_id;
|
||||
ATOM name_atom;
|
||||
BAN::String output_str;
|
||||
BAN::String mode_str;
|
||||
const DisplayInfo& info;
|
||||
};
|
||||
|
||||
static BAN::Vector<RANDRDisplay> s_randr_displays;
|
||||
static CARD32 s_timestamp { 0 };
|
||||
|
||||
static void initialize_displays()
|
||||
{
|
||||
for (size_t i = 0; i < g_displays.size(); i++)
|
||||
{
|
||||
auto name = MUST(BAN::String::formatted("B-OUT-{}", i));
|
||||
|
||||
const auto name_atom = g_atom_value++;
|
||||
MUST(g_atoms_name_to_id.insert(name, name_atom));
|
||||
MUST(g_atoms_id_to_name.insert(name_atom, name));
|
||||
|
||||
MUST(s_randr_displays.push_back({
|
||||
.crtc_id = g_next_global_id++,
|
||||
.output_id = g_next_global_id++,
|
||||
.mode_id = g_next_global_id++,
|
||||
.name_atom = name_atom,
|
||||
.output_str = BAN::move(name),
|
||||
.mode_str = MUST(BAN::String::formatted("{}x{}", g_displays[i].w, g_displays[i].h)),
|
||||
.info = g_displays[i],
|
||||
}));
|
||||
}
|
||||
|
||||
s_timestamp = time(nullptr);
|
||||
}
|
||||
|
||||
static const RANDRDisplay& find_display_by_output(CARD32 output)
|
||||
{
|
||||
for (const auto& display : s_randr_displays)
|
||||
if (display.output_id == output)
|
||||
return display;
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
static const RANDRDisplay& find_display_by_crtc(CARD32 crtc)
|
||||
{
|
||||
for (const auto& display : s_randr_displays)
|
||||
if (display.crtc_id == crtc)
|
||||
return display;
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpan packet)
|
||||
{
|
||||
if (s_randr_displays.empty())
|
||||
initialize_displays();
|
||||
static CARD32 crtc_id = 5;
|
||||
static CARD32 output_id = 6;
|
||||
static CARD32 mode_id = 7;
|
||||
static CARD32 timestamp = time(nullptr);
|
||||
|
||||
static xRenderTransform transform {
|
||||
1 << 16, 0, 0,
|
||||
@@ -113,14 +61,14 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
.setOfRotations = RR_Rotate_0,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 3,
|
||||
.root = g_root.windowId,
|
||||
.timestamp = s_timestamp,
|
||||
.configTimestamp = s_timestamp,
|
||||
.nSizes = 1,
|
||||
.sizeID = 0,
|
||||
.rotation = RR_Rotate_0,
|
||||
.rate = 60,
|
||||
.nrateEnts = 1,
|
||||
.root = g_root.windowId,
|
||||
.timestamp = timestamp,
|
||||
.configTimestamp = timestamp,
|
||||
.nSizes = 1,
|
||||
.sizeID = 0,
|
||||
.rotation = RR_Rotate_0,
|
||||
.rate = 60,
|
||||
.nrateEnts = 1,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
@@ -167,63 +115,55 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
dprintln("RRGetScreenResources{}", current ? "Current" : "");
|
||||
dprintln(" window: {}", request.window);
|
||||
|
||||
size_t mode_name_bytes = 0;
|
||||
for (const auto& display : s_randr_displays)
|
||||
mode_name_bytes += display.mode_str.size();
|
||||
const auto mode_name = TRY(BAN::String::formatted("{}x{}", g_root.pixWidth, g_root.pixHeight));
|
||||
|
||||
xRRGetScreenResourcesReply reply {
|
||||
.type = X_Reply,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = static_cast<CARD32>(s_randr_displays.size() * (1 + 1 + 8) + (mode_name_bytes + 3) / 4),
|
||||
.timestamp = s_timestamp,
|
||||
.configTimestamp = s_timestamp,
|
||||
.nCrtcs = static_cast<CARD16>(s_randr_displays.size()),
|
||||
.nOutputs = static_cast<CARD16>(s_randr_displays.size()),
|
||||
.nModes = static_cast<CARD16>(s_randr_displays.size()),
|
||||
.nbytesNames = static_cast<CARD16>(mode_name_bytes),
|
||||
.length = static_cast<CARD32>(1 + 1 + 8 + (mode_name.size() + 3) / 4),
|
||||
.timestamp = timestamp,
|
||||
.configTimestamp = timestamp,
|
||||
.nCrtcs = 1,
|
||||
.nOutputs = 1,
|
||||
.nModes = 1,
|
||||
.nbytesNames = static_cast<CARD16>(mode_name.size()),
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
for (const auto& display : s_randr_displays)
|
||||
TRY(encode(client_info.output_buffer, display.crtc_id));
|
||||
TRY(encode(client_info.output_buffer, crtc_id));
|
||||
|
||||
for (const auto& display : s_randr_displays)
|
||||
TRY(encode(client_info.output_buffer, display.output_id));
|
||||
TRY(encode(client_info.output_buffer, output_id));
|
||||
|
||||
for (const auto& display : s_randr_displays)
|
||||
{
|
||||
const CARD16 hsync_start = display.info.w;
|
||||
const CARD16 hsync_end = display.info.w + 1;
|
||||
const CARD16 htotal = display.info.w + 1;
|
||||
const CARD16 hsync_start = g_root.pixWidth;
|
||||
const CARD16 hsync_end = g_root.pixWidth + 1;
|
||||
const CARD16 htotal = g_root.pixWidth + 1;
|
||||
|
||||
const CARD16 vsync_start = display.info.h;
|
||||
const CARD16 vsync_end = display.info.h + 1;
|
||||
const CARD16 vtotal = display.info.h + 1;
|
||||
const CARD16 vsync_start = g_root.pixHeight;
|
||||
const CARD16 vsync_end = g_root.pixHeight + 1;
|
||||
const CARD16 vtotal = g_root.pixHeight + 1;
|
||||
|
||||
const CARD32 clock = htotal * vtotal * 60;
|
||||
const CARD32 clock = htotal * vtotal * 60;
|
||||
|
||||
xRRModeInfo mode_info {
|
||||
.id = display.mode_id,
|
||||
.width = static_cast<CARD16>(display.info.w),
|
||||
.height = static_cast<CARD16>(display.info.h),
|
||||
.dotClock = clock,
|
||||
.hSyncStart = hsync_start,
|
||||
.hSyncEnd = hsync_end,
|
||||
.hTotal = htotal,
|
||||
.hSkew = 0,
|
||||
.vSyncStart = vsync_start,
|
||||
.vSyncEnd = vsync_end,
|
||||
.vTotal = vtotal,
|
||||
.nameLength = static_cast<CARD16>(display.mode_str.size()),
|
||||
.modeFlags = RR_HSyncPositive | RR_VSyncPositive,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, mode_info));
|
||||
}
|
||||
xRRModeInfo mode_info {
|
||||
.id = mode_id,
|
||||
.width = g_root.pixWidth,
|
||||
.height = g_root.pixHeight,
|
||||
.dotClock = clock,
|
||||
.hSyncStart = hsync_start,
|
||||
.hSyncEnd = hsync_end,
|
||||
.hTotal = htotal,
|
||||
.hSkew = 0,
|
||||
.vSyncStart = vsync_start,
|
||||
.vSyncEnd = vsync_end,
|
||||
.vTotal = vtotal,
|
||||
.nameLength = static_cast<CARD16>(mode_name.size()),
|
||||
.modeFlags = RR_HSyncPositive | RR_VSyncPositive,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, mode_info));
|
||||
|
||||
for (const auto& display : s_randr_displays)
|
||||
TRY(encode(client_info.output_buffer, display.mode_str));
|
||||
while (client_info.output_buffer.size() % 4)
|
||||
TRY(encode<BYTE>(client_info.output_buffer, 0));
|
||||
TRY(encode(client_info.output_buffer, mode_name));
|
||||
for (size_t i = 0; (mode_name.size() + i) % 4; i++)
|
||||
TRY(encode(client_info.output_buffer, '\0'));
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -235,31 +175,27 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
dprintln(" output: {}", request.output);
|
||||
dprintln(" configTimestamp: {}", request.configTimestamp);
|
||||
|
||||
const auto& display = find_display_by_output(request.output);
|
||||
|
||||
xRRGetOutputInfoReply reply {
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = static_cast<CARD32>(1 + 1 + 1 + (display.output_str.size() + 3) / 4),
|
||||
.timestamp = s_timestamp,
|
||||
.crtc = display.crtc_id,
|
||||
.mmWidth = display.info.w * 254 / 960, // 96 DPI
|
||||
.mmHeight = display.info.h * 254 / 960, // 96 DPI
|
||||
.connection = RR_Connected,
|
||||
.subpixelOrder = SubPixelUnknown,
|
||||
.nCrtcs = 1,
|
||||
.nModes = 1,
|
||||
.nPreferred = 1,
|
||||
.nClones = 0,
|
||||
.nameLength = static_cast<CARD16>(display.output_str.size()),
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 1 + 1 + 1 + 2,
|
||||
.timestamp = timestamp,
|
||||
.crtc = crtc_id,
|
||||
.mmWidth = g_root.mmWidth,
|
||||
.mmHeight = g_root.mmHeight,
|
||||
.connection = RR_Connected,
|
||||
.subpixelOrder = SubPixelUnknown,
|
||||
.nCrtcs = 1,
|
||||
.nModes = 1,
|
||||
.nPreferred = 1,
|
||||
.nClones = 0,
|
||||
.nameLength = 5,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
TRY(encode(client_info.output_buffer, display.crtc_id));
|
||||
TRY(encode(client_info.output_buffer, display.mode_id));
|
||||
TRY(encode(client_info.output_buffer, display.output_str));
|
||||
while (client_info.output_buffer.size() % 4)
|
||||
TRY(encode<BYTE>(client_info.output_buffer, 0));
|
||||
TRY(encode(client_info.output_buffer, crtc_id));
|
||||
TRY(encode(client_info.output_buffer, mode_id));
|
||||
TRY(encode(client_info.output_buffer, "B-OUT\0\0\0"_sv));
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -314,27 +250,25 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
dprintln(" crtc: {}", request.crtc);
|
||||
dprintln(" configTimestamp: {}", request.configTimestamp);
|
||||
|
||||
const auto& display = find_display_by_crtc(request.crtc);
|
||||
|
||||
xRRGetCrtcInfoReply reply {
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 2,
|
||||
.timestamp = s_timestamp,
|
||||
.x = static_cast<INT16>(display.info.x),
|
||||
.y = static_cast<INT16>(display.info.y),
|
||||
.width = static_cast<CARD16>(display.info.w),
|
||||
.height = static_cast<CARD16>(display.info.h),
|
||||
.mode = display.mode_id,
|
||||
.timestamp = timestamp,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = g_root.pixWidth,
|
||||
.height = g_root.pixHeight,
|
||||
.mode = mode_id,
|
||||
.rotation = RR_Rotate_0,
|
||||
.rotations = RR_Rotate_0,
|
||||
.nOutput = 1,
|
||||
.nPossibleOutput = 1,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
TRY(encode(client_info.output_buffer, display.output_id));
|
||||
TRY(encode(client_info.output_buffer, display.output_id));
|
||||
TRY(encode(client_info.output_buffer, output_id));
|
||||
TRY(encode(client_info.output_buffer, output_id));
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -351,13 +285,13 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
dprintln(" mode: {}", request.mode);
|
||||
dprintln(" rotation: {}", request.rotation);
|
||||
|
||||
s_timestamp = time(nullptr);
|
||||
timestamp = time(nullptr);
|
||||
xRRSetCrtcConfigReply reply {
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 0,
|
||||
.newTimestamp = s_timestamp,
|
||||
.newTimestamp = timestamp,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
@@ -367,15 +301,15 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
{
|
||||
auto request = decode<xRRGetCrtcGammaSizeReq>(packet).value();
|
||||
|
||||
dwarnln("RRGetCrtcGammaSize");
|
||||
dwarnln(" crtc: {}", request.crtc);
|
||||
dprintln("RRGetCrtcGammaSize");
|
||||
dprintln(" crtc: {}", request.crtc);
|
||||
|
||||
xRRGetCrtcGammaSizeReply reply {
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 0,
|
||||
.size = 256,
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 0,
|
||||
.size = 1,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
@@ -389,16 +323,17 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
dprintln(" crtc: {}", request.crtc);
|
||||
|
||||
xRRGetCrtcGammaReply reply {
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = (6 * 256) / 4,
|
||||
.size = 256,
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 2,
|
||||
.size = 1,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
for (size_t j = 0; j < 256; j++)
|
||||
TRY(encode<CARD16>(client_info.output_buffer, j * 65535 / 255));
|
||||
TRY(encode<CARD16>(client_info.output_buffer, 1));
|
||||
TRY(encode<CARD16>(client_info.output_buffer, 1));
|
||||
TRY(encode<CARD16>(client_info.output_buffer, 1));
|
||||
TRY(encode<CARD16>(client_info.output_buffer, 0));
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -410,17 +345,17 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
dprintln(" crtc: {}", request.crtc);
|
||||
|
||||
xRRGetCrtcTransformReply reply {
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 16,
|
||||
.pendingTransform = transform,
|
||||
.hasTransforms = xFalse,
|
||||
.currentTransform = transform,
|
||||
.pendingNbytesFilter = 0,
|
||||
.pendingNparamsFilter = 0,
|
||||
.currentNbytesFilter = 0,
|
||||
.currentNparamsFilter = 0,
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 16,
|
||||
.pendingTransform = transform,
|
||||
.hasTransforms = xFalse,
|
||||
.currentTransform = transform,
|
||||
.pendingNbytesFilter = 0,
|
||||
.pendingNparamsFilter = 0,
|
||||
.currentNbytesFilter = 0,
|
||||
.currentNparamsFilter = 0,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
@@ -434,23 +369,23 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
dprintln(" crtc: {}", request.crtc);
|
||||
|
||||
xRRGetPanningReply reply {
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 1,
|
||||
.timestamp = s_timestamp,
|
||||
.left = 0,
|
||||
.top = 0,
|
||||
.width = 0,
|
||||
.height = 0,
|
||||
.track_left = 0,
|
||||
.track_top = 0,
|
||||
.track_width = 0,
|
||||
.track_height = 0,
|
||||
.border_left = 0,
|
||||
.border_top = 0,
|
||||
.border_right = 0,
|
||||
.border_bottom = 0,
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 1,
|
||||
.timestamp = timestamp,
|
||||
.left = 0,
|
||||
.top = 0,
|
||||
.width = 0,
|
||||
.height = 0,
|
||||
.track_left = 0,
|
||||
.track_top = 0,
|
||||
.track_width = 0,
|
||||
.track_height = 0,
|
||||
.border_left = 0,
|
||||
.border_top = 0,
|
||||
.border_right = 0,
|
||||
.border_bottom = 0,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
@@ -464,10 +399,10 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
dprintln(" window: {}", request.window);
|
||||
|
||||
xRRGetOutputPrimaryReply reply {
|
||||
.type = X_Reply,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 0,
|
||||
.output = s_randr_displays.front().output_id,
|
||||
.type = X_Reply,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 0,
|
||||
.output = output_id,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
@@ -481,10 +416,10 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
dprintln(" window: {}", request.window);
|
||||
|
||||
xRRGetProvidersReply reply {
|
||||
.type = X_Reply,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 0,
|
||||
.timestamp = s_timestamp,
|
||||
.type = X_Reply,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 0,
|
||||
.timestamp = timestamp,
|
||||
.nProviders = 0,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
@@ -500,33 +435,31 @@ static BAN::ErrorOr<void> extension_randr(Client& client_info, BAN::ConstByteSpa
|
||||
dprintln(" get_active: {}", request.get_active);
|
||||
|
||||
xRRGetMonitorsReply reply {
|
||||
.type = X_Reply,
|
||||
.type = X_Reply,
|
||||
.status = Success,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = static_cast<CARD32>(6 * s_randr_displays.size() + s_randr_displays.size()),
|
||||
.timestamp = s_timestamp,
|
||||
.nmonitors = static_cast<CARD32>(s_randr_displays.size()),
|
||||
.noutputs = static_cast<CARD32>(s_randr_displays.size()),
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 6 + 1,
|
||||
.timestamp = timestamp,
|
||||
.nmonitors = 1,
|
||||
.noutputs = 1,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
for (const auto& display : s_randr_displays)
|
||||
{
|
||||
xRRMonitorInfo monitor {
|
||||
.name = display.name_atom,
|
||||
.primary = (&display == &s_randr_displays.front()),
|
||||
.automatic = xTrue,
|
||||
.noutput = 1,
|
||||
.x = static_cast<INT16>(display.info.x),
|
||||
.y = static_cast<INT16>(display.info.y),
|
||||
.width = static_cast<CARD16>(display.info.w),
|
||||
.height = static_cast<CARD16>(display.info.h),
|
||||
.widthInMillimeters = display.info.w * 254 / 96, // 96 DPI
|
||||
.heightInMillimeters = display.info.h * 254 / 96, // 96 DPI
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, monitor));
|
||||
TRY(encode(client_info.output_buffer, display.output_id));
|
||||
}
|
||||
xRRMonitorInfo monitor {
|
||||
.name = None,
|
||||
.primary = xTrue,
|
||||
.automatic = xTrue,
|
||||
.noutput = 1,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = g_root.pixWidth,
|
||||
.height = g_root.pixHeight,
|
||||
.widthInMillimeters = g_root.mmWidth,
|
||||
.heightInMillimeters = g_root.mmHeight,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, monitor));
|
||||
|
||||
TRY(encode(client_info.output_buffer, output_id));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7,13 +7,42 @@
|
||||
#include <X11/X.h>
|
||||
#include <X11/extensions/shmproto.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static BYTE s_shm_event_base;
|
||||
static BYTE s_shm_error_base;
|
||||
static BYTE s_shm_major_opcode;
|
||||
|
||||
static bool is_local_socket(int socket)
|
||||
{
|
||||
sockaddr_storage addr;
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
if (getpeername(socket, reinterpret_cast<sockaddr*>(&addr), &addr_len) == -1)
|
||||
return false;
|
||||
|
||||
switch (addr.ss_family)
|
||||
{
|
||||
case AF_UNIX:
|
||||
return true;
|
||||
case AF_INET:
|
||||
{
|
||||
const auto* addr_in = reinterpret_cast<const sockaddr_in*>(&addr);
|
||||
const auto ipv4 = ntohl(addr_in->sin_addr.s_addr);
|
||||
return (ipv4 & IN_CLASSA_NET) == IN_LOOPBACKNET;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
const auto* addr_in6 = reinterpret_cast<const sockaddr_in6*>(&addr);
|
||||
return IN6_IS_ADDR_LOOPBACK(&addr_in6->sin6_addr);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<void*> get_shmseg(Client& client_info, CARD32 shmseg, BYTE op_major, BYTE op_minor)
|
||||
{
|
||||
auto it = g_objects.find(shmseg);
|
||||
@@ -49,7 +78,7 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
|
||||
|
||||
xShmQueryVersionReply reply {
|
||||
.type = X_Reply,
|
||||
.sharedPixmaps = false,
|
||||
.sharedPixmaps = is_local_socket(client_info.fd),
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 0,
|
||||
.majorVersion = 1,
|
||||
@@ -130,6 +159,7 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
|
||||
{
|
||||
auto request = decode<xShmPutImageReq>(packet).value();
|
||||
|
||||
#if 0
|
||||
dprintln("ShmPutImage");
|
||||
dprintln(" drawable: {}", request.drawable);
|
||||
dprintln(" gc: {}", request.gc);
|
||||
@@ -146,6 +176,7 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
|
||||
dprintln(" sendEvent: {}", request.sendEvent);
|
||||
dprintln(" shmseg: {}", request.shmseg);
|
||||
dprintln(" offset: {}", request.offset);
|
||||
#endif
|
||||
|
||||
void* shm_segment = TRY(get_shmseg(client_info, request.shmseg, op_major, op_minor));
|
||||
|
||||
@@ -236,6 +267,40 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
|
||||
|
||||
break;
|
||||
}
|
||||
case X_ShmCreatePixmap:
|
||||
{
|
||||
auto request = decode<xShmCreatePixmapReq>(packet).value();
|
||||
|
||||
dprintln("ShmCreatePixmap");
|
||||
dprintln(" depth: {}", request.depth);
|
||||
dprintln(" pid: {}", request.pid);
|
||||
dprintln(" drawable: {}", request.drawable);
|
||||
dprintln(" width: {}", request.width);
|
||||
dprintln(" height: {}", request.height);
|
||||
dprintln(" shmseg: {}", request.shmseg);
|
||||
dprintln(" offset: {}", request.offset);
|
||||
|
||||
ASSERT(request.depth == 24 || request.depth == 32);
|
||||
|
||||
void* shm_segment = TRY(get_shmseg(client_info, request.shmseg, op_major, op_minor));
|
||||
|
||||
TRY(client_info.objects.insert(request.pid));
|
||||
TRY(g_objects.insert(
|
||||
request.pid,
|
||||
TRY(BAN::UniqPtr<Object>::create(Object {
|
||||
.type = Object::Type::Pixmap,
|
||||
.object = Object::Pixmap {
|
||||
.depth = request.depth,
|
||||
.width = request.width,
|
||||
.height = request.height,
|
||||
.data = BAN::ByteSpan(static_cast<uint8_t*>(shm_segment) + request.offset, request.width * request.height * 4),
|
||||
.owned_data = {},
|
||||
}
|
||||
}))
|
||||
));
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
dwarnln("unsupported shm minor opcode {}", packet[1]);
|
||||
break;
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
#include "Extensions.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <X11/X.h>
|
||||
#include <X11/extensions/XResproto.h>
|
||||
|
||||
template<typename F>
|
||||
static BAN::ErrorOr<void> for_each_client(uint32_t target_spec, const F& callback)
|
||||
{
|
||||
for (const auto& [fd, thingy] : g_epoll_thingies)
|
||||
{
|
||||
if (thingy.type != EpollThingy::Type::Client)
|
||||
continue;
|
||||
|
||||
const auto& client_info = thingy.value.get<Client>();
|
||||
if (target_spec && (target_spec >> 20) != client_info.fd)
|
||||
continue;
|
||||
|
||||
TRY(callback(client_info, target_spec ? target_spec : (client_info.fd << 20)));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> extension_xres(Client& client_info, BAN::ConstByteSpan packet)
|
||||
{
|
||||
const uint8_t major_opcode = packet[0];
|
||||
const uint8_t minor_opcode = packet[1];
|
||||
|
||||
switch (minor_opcode)
|
||||
{
|
||||
case X_XResQueryVersion:
|
||||
{
|
||||
auto request = decode<xXResQueryVersionReq>(packet).value();
|
||||
|
||||
dprintln("XResQueryVersion");
|
||||
dprintln(" clientMajor: {}", request.client_major);
|
||||
dprintln(" clientMinor: {}", request.client_minor);
|
||||
|
||||
xXResQueryVersionReply reply {
|
||||
.type = X_Reply,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 0,
|
||||
.server_major = 1,
|
||||
.server_minor = 2,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
break;
|
||||
}
|
||||
case X_XResQueryClientIds:
|
||||
{
|
||||
auto request = decode<xXResQueryClientIdsReq>(packet).value();
|
||||
|
||||
dprintln("XResQueryClientIds");
|
||||
dprintln(" numSpecs: {}", request.numSpecs);
|
||||
|
||||
uint32_t num_ids { 0 };
|
||||
BAN::Vector<uint8_t> query_result;
|
||||
|
||||
for (size_t i = 0; i < request.numSpecs; i++)
|
||||
{
|
||||
auto spec = decode<xXResClientIdSpec>(packet).value();
|
||||
|
||||
TRY(for_each_client(spec.client, [&](const Client& client_info, uint32_t client_spec) -> BAN::ErrorOr<void> {
|
||||
if (spec.mask == None || (spec.mask & X_XResClientXIDMask))
|
||||
{
|
||||
xXResClientIdValue value {
|
||||
.spec = {
|
||||
.client = client_spec,
|
||||
.mask = X_XResClientXIDMask,
|
||||
},
|
||||
.length = 0,
|
||||
};
|
||||
TRY(encode(query_result, value));
|
||||
num_ids++;
|
||||
}
|
||||
if ((spec.mask == None || (spec.mask & X_XResLocalClientPIDMask)) && client_info.pid.has_value())
|
||||
{
|
||||
xXResClientIdValue value {
|
||||
.spec = {
|
||||
.client = client_spec,
|
||||
.mask = X_XResLocalClientPIDMask,
|
||||
},
|
||||
.length = 4,
|
||||
};
|
||||
TRY(encode(query_result, value));
|
||||
TRY(encode(query_result, client_info.pid.value()));
|
||||
num_ids++;
|
||||
}
|
||||
return {};
|
||||
}));
|
||||
}
|
||||
|
||||
xXResQueryClientIdsReply reply {
|
||||
.type = X_Reply,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = static_cast<uint32_t>(query_result.size() / 4),
|
||||
.numIds = num_ids,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
TRY(encode(client_info.output_buffer, query_result));
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
dwarnln("unsupported opcode XRes minor opcode {}", minor_opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static struct XResInstaller
|
||||
{
|
||||
XResInstaller()
|
||||
{
|
||||
install_extension(XRES_NAME, 6, 0, extension_xres);
|
||||
}
|
||||
} installer;
|
||||
@@ -385,15 +385,13 @@ static BAN::ErrorOr<BAN::RefPtr<PCFFont>> parse_font(const BAN::String& path)
|
||||
font->font_ascent = font->max_bounds.ascent;
|
||||
font->font_descent = font->max_bounds.descent;
|
||||
|
||||
font->is_cursor_font = (path == FONT_PATH "/misc/cursor.pcf.gz"_sv);
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
__attribute__((constructor))
|
||||
static void initialize_fonts()
|
||||
{
|
||||
const char* font_path = FONT_PATH "/misc";
|
||||
const char* font_path = "fonts/misc";
|
||||
|
||||
do
|
||||
{
|
||||
@@ -786,7 +784,7 @@ static void write_text(WriteTextInfo& info)
|
||||
? info.gc.foreground
|
||||
: info.gc.background;
|
||||
|
||||
if (color != COLOR_INVISIBLE)
|
||||
if (color != LibGUI::Texture::color_invisible)
|
||||
info.out_data_u32[out_y * info.out_w + out_x] = color;
|
||||
}
|
||||
}
|
||||
@@ -940,49 +938,6 @@ BAN::ErrorOr<void> create_glyph_cursor(Client& client_info, BAN::ConstByteSpan p
|
||||
|
||||
const auto& source_font = TRY(get_fontable(client_info, request.source, X_CreateGlyphCursor));
|
||||
|
||||
// Try to use system cursor for known cursors
|
||||
if (source_font->is_cursor_font && request.mask == request.source && request.sourceChar + 1 == request.maskChar && g_platform_ops.create_system_cursor)
|
||||
{
|
||||
BAN::Optional<SystemCursorType> type;
|
||||
switch (request.sourceChar)
|
||||
{
|
||||
case 68: type = SystemCursorType::Pointer; break;
|
||||
case 152: type = SystemCursorType::Text; break;
|
||||
case 150: type = SystemCursorType::Wait; break;
|
||||
case 60: type = SystemCursorType::Hand; break;
|
||||
case 92: type = SystemCursorType::Help; break;
|
||||
case 52: type = SystemCursorType::Move; break;
|
||||
case 88: type = SystemCursorType::Forbidden; break;
|
||||
case 138: type = SystemCursorType::ResizeN; break;
|
||||
case 96: type = SystemCursorType::ResizeE; break;
|
||||
case 16: type = SystemCursorType::ResizeS; break;
|
||||
case 70: type = SystemCursorType::ResizeW; break;
|
||||
case 134: type = SystemCursorType::ResizeNW; break;
|
||||
case 136: type = SystemCursorType::ResizeNE; break;
|
||||
case 12: type = SystemCursorType::ResizeSW; break;
|
||||
case 14: type = SystemCursorType::ResizeSE; break;
|
||||
case 108: type = SystemCursorType::ResizeHorizontal; break;
|
||||
case 116: type = SystemCursorType::ResizeVertical; break;
|
||||
}
|
||||
|
||||
if (type.has_value())
|
||||
{
|
||||
auto cursor_or_error = g_platform_ops.create_system_cursor(type.value());
|
||||
if (!cursor_or_error.is_error())
|
||||
{
|
||||
TRY(client_info.objects.insert(request.cid));
|
||||
TRY(g_objects.insert(
|
||||
request.cid,
|
||||
TRY(BAN::UniqPtr<Object>::create(Object {
|
||||
.type = Object::Type::Cursor,
|
||||
.object = BAN::move(cursor_or_error.value()),
|
||||
}))
|
||||
));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto source_glyph_index = source_font->find_glyph(request.sourceChar);
|
||||
if (!source_glyph_index.has_value())
|
||||
{
|
||||
@@ -1003,14 +958,19 @@ BAN::ErrorOr<void> create_glyph_cursor(Client& client_info, BAN::ConstByteSpan p
|
||||
const uint32_t source_width = source_ci.rightSideBearing - source_ci.leftSideBearing;
|
||||
const uint32_t source_height = source_ci.ascent + source_ci.descent;
|
||||
|
||||
BAN::Vector<uint32_t> pixels;
|
||||
TRY(pixels.resize(source_width * source_height));
|
||||
Object::Cursor cursor {
|
||||
.width = source_width,
|
||||
.height = source_height,
|
||||
.origin_x = -source_ci.leftSideBearing,
|
||||
.origin_y = source_ci.ascent,
|
||||
};
|
||||
TRY(cursor.pixels.resize(cursor.width * cursor.height));
|
||||
|
||||
for (uint32_t y = 0; y < source_height; y++)
|
||||
{
|
||||
const uint8_t* row_base = source_font->bitmap.data() + source_glyph.bitmap_offset + (source_width + 7) / 8 * y;
|
||||
for (uint32_t x = 0; x < source_width; x++)
|
||||
pixels[y * source_width + x] = 0xFF000000 | ((row_base[x / 8] & (1 << (x % 8))) ? foreground : background);
|
||||
cursor.pixels[y * source_width + x] = 0xFF000000 | ((row_base[x / 8] & (1 << (x % 8))) ? foreground : background);
|
||||
}
|
||||
|
||||
if (request.mask != None)
|
||||
@@ -1048,19 +1008,11 @@ BAN::ErrorOr<void> create_glyph_cursor(Client& client_info, BAN::ConstByteSpan p
|
||||
|
||||
const uint8_t* row_base = mask_font->bitmap.data() + mask_glyph.bitmap_offset + (mask_width + 7) / 8 * mask_y;
|
||||
if (!(row_base[mask_x / 8] & (1 << (mask_x % 8))))
|
||||
pixels[src_y * source_width + src_x] = 0;
|
||||
cursor.pixels[src_y * source_width + src_x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::UniqPtr<PlatformCursor> cursor;
|
||||
if (g_platform_ops.create_bitmap_cursor)
|
||||
{
|
||||
auto cursor_or_error = g_platform_ops.create_bitmap_cursor(pixels.data(), source_width, source_height, -source_ci.leftSideBearing, source_ci.ascent);
|
||||
if (!cursor_or_error.is_error())
|
||||
cursor = cursor_or_error.release_value();
|
||||
}
|
||||
|
||||
TRY(client_info.objects.insert(request.cid));
|
||||
TRY(g_objects.insert(
|
||||
request.cid,
|
||||
|
||||
@@ -40,8 +40,6 @@ struct PCFFont : public BAN::RefCounted<PCFFont>, public BAN::Weakable<PCFFont>
|
||||
BAN::Vector<MapEntry> map;
|
||||
BAN::Vector<uint8_t> bitmap;
|
||||
|
||||
bool is_cursor_font;
|
||||
|
||||
BAN::Optional<uint16_t> find_glyph(uint16_t codepoint) const
|
||||
{
|
||||
size_t l = 0;
|
||||
|
||||
@@ -60,33 +60,8 @@ void put_image(const PutImageInfo& info)
|
||||
}
|
||||
case ZPixmap:
|
||||
{
|
||||
bool needs_clipping = true;
|
||||
|
||||
if (info.gc.clip_mask == None)
|
||||
{
|
||||
needs_clipping = false;
|
||||
}
|
||||
else if (info.gc.clip_mask == UINT32_MAX)
|
||||
{
|
||||
const uint32_t clip_min_x = min_x - info.gc.clip_origin_x;
|
||||
const uint32_t clip_min_y = min_y - info.gc.clip_origin_y;
|
||||
|
||||
const uint32_t clip_max_x = max_x - info.gc.clip_origin_x;
|
||||
const uint32_t clip_max_y = max_y - info.gc.clip_origin_y;
|
||||
|
||||
for (const auto& rect : info.gc.clip_rects)
|
||||
{
|
||||
if (clip_min_x < rect.x || clip_max_x > rect.x + rect.width)
|
||||
continue;
|
||||
if (clip_min_y < rect.y || clip_max_y > rect.y + rect.height)
|
||||
continue;
|
||||
needs_clipping = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(info.left_pad == 0);
|
||||
if (in_bpp == 32 && !needs_clipping)
|
||||
if (in_bpp == 32 && info.gc.clip_mask == None)
|
||||
{
|
||||
const auto bytes_per_row = (max_x - min_x) * 4;
|
||||
for (int32_t y = min_y; y < max_y; y++)
|
||||
|
||||
@@ -1,5 +1,347 @@
|
||||
#include "Keymap.h"
|
||||
|
||||
uint32_t g_keymap[g_keymap_size][g_keymap_layers] {};
|
||||
uint8_t g_modifier_map[8][2] {};
|
||||
uint8_t g_pressed_keys[32] {};
|
||||
#include <BAN/StringView.h>
|
||||
|
||||
#include <LibInput/KeyboardLayout.h>
|
||||
|
||||
#include <X11/X.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/XF86keysym.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#undef None
|
||||
|
||||
uint32_t g_keymap[0x100][g_keymap_layers];
|
||||
|
||||
static constexpr uint32_t my_key_to_x_keysym(LibInput::Key key)
|
||||
{
|
||||
using LibInput::Key;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case Key::A:
|
||||
return XK_A;
|
||||
case Key::B:
|
||||
return XK_B;
|
||||
case Key::C:
|
||||
return XK_C;
|
||||
case Key::D:
|
||||
return XK_D;
|
||||
case Key::E:
|
||||
return XK_E;
|
||||
case Key::F:
|
||||
return XK_F;
|
||||
case Key::G:
|
||||
return XK_G;
|
||||
case Key::H:
|
||||
return XK_H;
|
||||
case Key::I:
|
||||
return XK_I;
|
||||
case Key::J:
|
||||
return XK_J;
|
||||
case Key::K:
|
||||
return XK_K;
|
||||
case Key::L:
|
||||
return XK_L;
|
||||
case Key::M:
|
||||
return XK_M;
|
||||
case Key::N:
|
||||
return XK_N;
|
||||
case Key::O:
|
||||
return XK_O;
|
||||
case Key::P:
|
||||
return XK_P;
|
||||
case Key::Q:
|
||||
return XK_Q;
|
||||
case Key::R:
|
||||
return XK_R;
|
||||
case Key::S:
|
||||
return XK_S;
|
||||
case Key::T:
|
||||
return XK_T;
|
||||
case Key::U:
|
||||
return XK_U;
|
||||
case Key::V:
|
||||
return XK_V;
|
||||
case Key::W:
|
||||
return XK_W;
|
||||
case Key::X:
|
||||
return XK_X;
|
||||
case Key::Y:
|
||||
return XK_Y;
|
||||
case Key::Z:
|
||||
return XK_Z;
|
||||
case Key::A_Ring:
|
||||
return XK_Aring;
|
||||
case Key::A_Umlaut:
|
||||
return XK_Adiaeresis;
|
||||
case Key::O_Umlaut:
|
||||
return XK_Odiaeresis;
|
||||
case Key::_0:
|
||||
return XK_0;
|
||||
case Key::_1:
|
||||
return XK_1;
|
||||
case Key::_2:
|
||||
return XK_2;
|
||||
case Key::_3:
|
||||
return XK_3;
|
||||
case Key::_4:
|
||||
return XK_4;
|
||||
case Key::_5:
|
||||
return XK_5;
|
||||
case Key::_6:
|
||||
return XK_6;
|
||||
case Key::_7:
|
||||
return XK_7;
|
||||
case Key::_8:
|
||||
return XK_8;
|
||||
case Key::_9:
|
||||
return XK_9;
|
||||
case Key::F1:
|
||||
return XK_F1;
|
||||
case Key::F2:
|
||||
return XK_F2;
|
||||
case Key::F3:
|
||||
return XK_F3;
|
||||
case Key::F4:
|
||||
return XK_F4;
|
||||
case Key::F5:
|
||||
return XK_F5;
|
||||
case Key::F6:
|
||||
return XK_F6;
|
||||
case Key::F7:
|
||||
return XK_F7;
|
||||
case Key::F8:
|
||||
return XK_F8;
|
||||
case Key::F9:
|
||||
return XK_F9;
|
||||
case Key::F10:
|
||||
return XK_F10;
|
||||
case Key::F11:
|
||||
return XK_F11;
|
||||
case Key::F12:
|
||||
return XK_F12;
|
||||
case Key::Insert:
|
||||
return XK_Insert;
|
||||
case Key::PrintScreen:
|
||||
return XK_Print;
|
||||
case Key::Delete:
|
||||
return XK_Delete;
|
||||
case Key::Home:
|
||||
return XK_Home;
|
||||
case Key::End:
|
||||
return XK_End;
|
||||
case Key::PageUp:
|
||||
return XK_Page_Up;
|
||||
case Key::PageDown:
|
||||
return XK_Page_Down;
|
||||
case Key::Enter:
|
||||
return XK_Return;
|
||||
case Key::Space:
|
||||
return XK_space;
|
||||
case Key::ExclamationMark:
|
||||
return XK_exclam;
|
||||
case Key::DoubleQuote:
|
||||
return XK_quotedbl;
|
||||
case Key::Hashtag:
|
||||
return XK_numbersign;
|
||||
case Key::Currency:
|
||||
return XK_currency;
|
||||
case Key::Percent:
|
||||
return XK_percent;
|
||||
case Key::Ampersand:
|
||||
return XK_ampersand;
|
||||
case Key::Slash:
|
||||
return XK_slash;
|
||||
case Key::Section:
|
||||
return XK_section;
|
||||
case Key::Half:
|
||||
return XK_onehalf;
|
||||
case Key::OpenParenthesis:
|
||||
return '(';
|
||||
case Key::CloseParenthesis:
|
||||
return ')';
|
||||
case Key::OpenSquareBracket:
|
||||
return '[';
|
||||
case Key::CloseSquareBracket:
|
||||
return ']';
|
||||
case Key::OpenCurlyBracket:
|
||||
return '{';
|
||||
case Key::CloseCurlyBracket:
|
||||
return '}';
|
||||
case Key::Equals:
|
||||
return '=';
|
||||
case Key::QuestionMark:
|
||||
return '?';
|
||||
case Key::Plus:
|
||||
return '+';
|
||||
case Key::BackSlash:
|
||||
return '\\';
|
||||
case Key::Acute:
|
||||
return XK_acute;
|
||||
case Key::BackTick:
|
||||
return '`';
|
||||
case Key::TwoDots:
|
||||
return XK_diaeresis;
|
||||
case Key::Cedilla:
|
||||
return XK_Ccedilla;
|
||||
case Key::Backspace:
|
||||
return XK_BackSpace;
|
||||
case Key::AtSign:
|
||||
return XK_at;
|
||||
case Key::Pound:
|
||||
return XK_sterling;
|
||||
case Key::Dollar:
|
||||
return XK_dollar;
|
||||
case Key::Euro:
|
||||
return XK_EuroSign;
|
||||
case Key::Escape:
|
||||
return XK_Escape;
|
||||
case Key::Tab:
|
||||
return XK_Tab;
|
||||
case Key::CapsLock:
|
||||
return XK_Caps_Lock;
|
||||
case Key::LeftShift:
|
||||
return XK_Shift_L;
|
||||
case Key::LeftCtrl:
|
||||
return XK_Control_L;
|
||||
case Key::Super:
|
||||
return XK_Super_L;
|
||||
case Key::LeftAlt:
|
||||
return XK_Alt_L;
|
||||
case Key::RightAlt:
|
||||
return XK_Alt_R;
|
||||
case Key::RightCtrl:
|
||||
return XK_Control_R;
|
||||
case Key::RightShift:
|
||||
return XK_Shift_R;
|
||||
case Key::SingleQuote:
|
||||
return '\'';
|
||||
case Key::Asterix:
|
||||
return '*';
|
||||
case Key::Caret:
|
||||
return '^';
|
||||
case Key::Tilde:
|
||||
return '~';
|
||||
case Key::ArrowUp:
|
||||
return XK_Up;
|
||||
case Key::ArrowDown:
|
||||
return XK_Down;
|
||||
case Key::ArrowLeft:
|
||||
return XK_Left;
|
||||
case Key::ArrowRight:
|
||||
return XK_Right;
|
||||
case Key::Comma:
|
||||
return ',';
|
||||
case Key::Semicolon:
|
||||
return ';';
|
||||
case Key::Period:
|
||||
return '.';
|
||||
case Key::Colon:
|
||||
return ':';
|
||||
case Key::Hyphen:
|
||||
return '-';
|
||||
case Key::Underscore:
|
||||
return '_';
|
||||
case Key::NumLock:
|
||||
return XK_Num_Lock;
|
||||
case Key::ScrollLock:
|
||||
return XK_Scroll_Lock;
|
||||
case Key::LessThan:
|
||||
return '<';
|
||||
case Key::GreaterThan:
|
||||
return '>';
|
||||
case Key::Pipe:
|
||||
return '|';
|
||||
case Key::Negation:
|
||||
return XK_notsign;
|
||||
case Key::BrokenBar:
|
||||
return XK_brokenbar;
|
||||
case Key::Numpad0:
|
||||
return XK_KP_0;
|
||||
case Key::Numpad1:
|
||||
return XK_KP_1;
|
||||
case Key::Numpad2:
|
||||
return XK_KP_2;
|
||||
case Key::Numpad3:
|
||||
return XK_KP_3;
|
||||
case Key::Numpad4:
|
||||
return XK_KP_4;
|
||||
case Key::Numpad5:
|
||||
return XK_KP_5;
|
||||
case Key::Numpad6:
|
||||
return XK_KP_6;
|
||||
case Key::Numpad7:
|
||||
return XK_KP_7;
|
||||
case Key::Numpad8:
|
||||
return XK_KP_8;
|
||||
case Key::Numpad9:
|
||||
return XK_KP_9;
|
||||
case Key::NumpadPlus:
|
||||
return XK_KP_Add;
|
||||
case Key::NumpadMinus:
|
||||
return XK_KP_Subtract;
|
||||
case Key::NumpadMultiply:
|
||||
return XK_KP_Multiply;
|
||||
case Key::NumpadDivide:
|
||||
return XK_KP_Divide;
|
||||
case Key::NumpadEnter:
|
||||
return XK_KP_Enter;
|
||||
case Key::NumpadDecimal:
|
||||
return XK_KP_Decimal;
|
||||
case Key::VolumeMute:
|
||||
return XF86XK_AudioMute;
|
||||
case Key::VolumeUp:
|
||||
return XF86XK_AudioRaiseVolume;
|
||||
case Key::VolumeDown:
|
||||
return XF86XK_AudioLowerVolume;
|
||||
case Key::Calculator:
|
||||
return XF86XK_Calculator;
|
||||
case Key::MediaPlayPause:
|
||||
return XF86XK_AudioPlay;
|
||||
case Key::MediaStop:
|
||||
return XF86XK_AudioStop;
|
||||
case Key::MediaPrevious:
|
||||
return XF86XK_AudioPrev;
|
||||
case Key::MediaNext:
|
||||
return XF86XK_AudioNext;
|
||||
|
||||
case Key::Invalid:
|
||||
case Key::None:
|
||||
case Key::Count:
|
||||
break;
|
||||
}
|
||||
|
||||
return NoSymbol;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> initialize_keymap()
|
||||
{
|
||||
for (auto& keycode : g_keymap)
|
||||
for (auto& keysym : keycode)
|
||||
keysym = NoSymbol;
|
||||
|
||||
// FIXME: get this from somewhere (gui command? enviroment? tmp file?)
|
||||
const auto keymap_path = "./us.keymap"_sv;
|
||||
|
||||
TRY(LibInput::KeyboardLayout::initialize());
|
||||
|
||||
auto& keyboard_layout = LibInput::KeyboardLayout::get();
|
||||
TRY(keyboard_layout.load_from_file(keymap_path));
|
||||
|
||||
const BAN::Span<const LibInput::Key> my_keymaps[] {
|
||||
keyboard_layout.keymap_normal(),
|
||||
keyboard_layout.keymap_shift(),
|
||||
keyboard_layout.keymap_altgr(),
|
||||
keyboard_layout.keymap_altgr(), // add shift+altgr map?
|
||||
};
|
||||
|
||||
for (size_t keycode = g_keymap_min_keycode; keycode < g_keymap_max_keycode; keycode++)
|
||||
for (size_t layer = 0; layer < g_keymap_layers; layer++)
|
||||
if (auto my_key = my_keymaps[layer][keycode - g_keymap_min_keycode]; my_key != LibInput::Key::None)
|
||||
if (auto keysym = my_key_to_x_keysym(my_key); keysym != NoSymbol)
|
||||
g_keymap[keycode][layer] = (layer % 2) ? toupper(keysym) : tolower(keysym);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
BAN::ErrorOr<void> initialize_keymap();
|
||||
|
||||
constexpr size_t g_keymap_min_keycode = 8;
|
||||
constexpr size_t g_keymap_max_keycode = 255;
|
||||
constexpr size_t g_keymap_size = g_keymap_max_keycode - g_keymap_min_keycode + 1;
|
||||
|
||||
constexpr size_t g_keymap_layers = 4;
|
||||
extern uint32_t g_keymap[g_keymap_size][g_keymap_layers];
|
||||
|
||||
// shift, capslock, control, alt, numlock, level5 shift, super, altgr with 2 keycodes per modifier
|
||||
constexpr size_t g_modifier_layers = 2;
|
||||
extern uint8_t g_modifier_map[8][g_modifier_layers];
|
||||
|
||||
extern uint8_t g_pressed_keys[32];
|
||||
extern uint32_t g_keymap[0x100][g_keymap_layers];
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#include <BAN/UniqPtr.h>
|
||||
|
||||
struct PlatformWindow
|
||||
{
|
||||
virtual ~PlatformWindow() = default;
|
||||
};
|
||||
|
||||
struct PlatformCursor
|
||||
{
|
||||
virtual ~PlatformCursor() = default;
|
||||
};
|
||||
|
||||
enum class WindowType
|
||||
{
|
||||
Normal,
|
||||
Utility,
|
||||
Popup,
|
||||
};
|
||||
|
||||
enum class SystemCursorType
|
||||
{
|
||||
Pointer,
|
||||
Text,
|
||||
Wait,
|
||||
Hand,
|
||||
Help,
|
||||
Move,
|
||||
Forbidden,
|
||||
ResizeN,
|
||||
ResizeE,
|
||||
ResizeS,
|
||||
ResizeW,
|
||||
ResizeNW,
|
||||
ResizeNE,
|
||||
ResizeSW,
|
||||
ResizeSE,
|
||||
ResizeVertical,
|
||||
ResizeHorizontal,
|
||||
};
|
||||
|
||||
// initialize, create_window and invalidate are required
|
||||
// poll_events is required when an event fd is registered
|
||||
struct PlatformOps
|
||||
{
|
||||
/* Do platform initialization */
|
||||
bool (*initialize)();
|
||||
/* Handle pending events */
|
||||
void (*poll_events)(void*);
|
||||
/* Create a window with given size */
|
||||
BAN::ErrorOr<BAN::UniqPtr<PlatformWindow>> (*create_window)(WindowType, WINDOW wid, int32_t x, int32_t y, uint32_t width, uint32_t height);
|
||||
/* All invalidate calls during one frame happen between calls to begin_frame and end_frame. pixels is in ARGB8888
|
||||
* invalidate should update the underlying textures
|
||||
* begin_frame should do any necessary preparations to update a frame
|
||||
* end_frame should flush the updated texturee to the window */
|
||||
void (*invalidate)(PlatformWindow*, const void* pixels, uint32_t pitch, uint32_t x, uint32_t y, uint32_t width, uint32_t height);
|
||||
void (*begin_frame)(PlatformWindow*);
|
||||
void (*end_frame)(PlatformWindow*);
|
||||
/* Request resize of a window, can be async */
|
||||
void (*request_resize)(PlatformWindow*, uint32_t width, uint32_t height);
|
||||
/* Request window repositioning */
|
||||
void (*request_reposition)(PlatformWindow*, int32_t x, int32_t y);
|
||||
/* Request new fullscreen state, can be async */
|
||||
void (*request_fullscreen)(PlatformWindow*, bool fullscreen);
|
||||
/* Warp pointer */
|
||||
void (*warp_pointer)(int32_t x, int32_t y, bool relative);
|
||||
/* Get global position of the pointer */
|
||||
void (*query_pointer)(int32_t* x, int32_t* y);
|
||||
/* Grab pointer events on window */
|
||||
void (*set_pointer_grab)(PlatformWindow*, bool grabbed);
|
||||
/* Create a system cursor */
|
||||
BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> (*create_system_cursor)(SystemCursorType);
|
||||
/* Create cursor from custom bitmap */
|
||||
BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> (*create_bitmap_cursor)(const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y);
|
||||
/* Set custom cursor */
|
||||
void (*set_cursor)(PlatformWindow*, PlatformCursor*);
|
||||
};
|
||||
extern PlatformOps g_platform_ops;
|
||||
|
||||
void register_display(int32_t x, int32_t y, uint32_t width, uint32_t height);
|
||||
@@ -1,705 +0,0 @@
|
||||
#include "../Events.h"
|
||||
#include "../Keymap.h"
|
||||
#include "../Platform.h"
|
||||
|
||||
#include <BAN/Atomic.h>
|
||||
#include <BAN/HashMap.h>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
BAN::HashMap<uint32_t, struct SDLWindow*> s_window_map;
|
||||
|
||||
struct SDLWindow final : public PlatformWindow
|
||||
{
|
||||
~SDLWindow()
|
||||
{
|
||||
if (texture != nullptr)
|
||||
SDL_DestroyTexture(texture);
|
||||
|
||||
if (renderer != nullptr)
|
||||
SDL_DestroyRenderer(renderer);
|
||||
|
||||
if (window != nullptr)
|
||||
{
|
||||
s_window_map.remove(SDL_GetWindowID(window));
|
||||
SDL_DestroyWindow(window);
|
||||
}
|
||||
}
|
||||
|
||||
WINDOW wid;
|
||||
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
|
||||
SDL_Window* window { nullptr };
|
||||
SDL_Renderer* renderer { nullptr };
|
||||
SDL_Texture* texture { nullptr };
|
||||
};
|
||||
|
||||
struct SDLCursor final : public PlatformCursor
|
||||
{
|
||||
~SDLCursor()
|
||||
{
|
||||
if (cursor != nullptr)
|
||||
SDL_DestroyCursor(cursor);
|
||||
}
|
||||
|
||||
SDL_Cursor* cursor { nullptr };
|
||||
};
|
||||
|
||||
static int s_eventfd { -1 };
|
||||
|
||||
static SDL_Cursor* s_default_cursor { nullptr };
|
||||
|
||||
static void* sdl3_thread(void*)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
const uint64_t value { 1 };
|
||||
write(s_eventfd, &value, sizeof(value));
|
||||
usleep(16'666);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void sdl3_initialize_keymap();
|
||||
|
||||
static bool sdl3_initialize()
|
||||
{
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS))
|
||||
{
|
||||
dwarnln("Could not initialize SDL: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
const SDL_DisplayID* display_ids = SDL_GetDisplays(nullptr);
|
||||
for (int i = 0; display_ids[i]; i++)
|
||||
{
|
||||
SDL_Rect rect;
|
||||
if (!SDL_GetDisplayBounds(display_ids[i], &rect))
|
||||
{
|
||||
dwarnln("Could not display {} bounds: {}", SDL_GetError());
|
||||
continue;
|
||||
}
|
||||
register_display(rect.x, rect.y, rect.w, rect.h);
|
||||
}
|
||||
|
||||
sdl3_initialize_keymap();
|
||||
|
||||
s_default_cursor = SDL_GetCursor();
|
||||
|
||||
s_eventfd = eventfd(0, 0);
|
||||
if (s_eventfd == -1)
|
||||
{
|
||||
dwarnln("Could not create eventfd: {}", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
register_event_fd(s_eventfd, nullptr);
|
||||
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, nullptr, sdl3_thread, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::UniqPtr<PlatformWindow>> sdl3_create_window(WindowType type, WINDOW wid, int32_t x, int32_t y, uint32_t width, uint32_t height)
|
||||
{
|
||||
auto window = TRY(BAN::UniqPtr<SDLWindow>::create());
|
||||
|
||||
window->wid = wid;
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
|
||||
int flags;
|
||||
switch (type)
|
||||
{
|
||||
case WindowType::Normal:
|
||||
flags = SDL_WINDOW_RESIZABLE;
|
||||
break;
|
||||
case WindowType::Utility:
|
||||
flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_UTILITY;
|
||||
break;
|
||||
case WindowType::Popup:
|
||||
flags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_UTILITY;
|
||||
break;
|
||||
}
|
||||
|
||||
flags |= SDL_WINDOW_TRANSPARENT;
|
||||
|
||||
window->window = SDL_CreateWindow("", width, height, flags);
|
||||
|
||||
if (window->window == nullptr)
|
||||
{
|
||||
dwarnln("Could not create SDL window: {}", SDL_GetError());
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
if (x != 0 || y != 0)
|
||||
SDL_SetWindowPosition(window->window, x, y);
|
||||
|
||||
window->renderer = SDL_CreateRenderer(window->window, nullptr);
|
||||
if (window->renderer == nullptr)
|
||||
{
|
||||
dwarnln("Could not create SDL renderer: {}", SDL_GetError());
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawColor(window->renderer, 0, 0, 0, 0);
|
||||
|
||||
window->texture = SDL_CreateTexture(window->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||
if (window->texture == nullptr)
|
||||
{
|
||||
dwarnln("Could not create SDL texture: {}", SDL_GetError());
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
TRY(s_window_map.insert(SDL_GetWindowID(window->window), window.ptr()));
|
||||
|
||||
return BAN::UniqPtr<PlatformWindow>(BAN::move(window));
|
||||
}
|
||||
|
||||
static void sdl3_request_resize(PlatformWindow* window, uint32_t width, uint32_t height)
|
||||
{
|
||||
auto& sdl_window = *static_cast<SDLWindow*>(window);
|
||||
SDL_SetWindowSize(sdl_window.window, width, height);
|
||||
}
|
||||
|
||||
static void sdl3_request_reposition(PlatformWindow* window, int32_t x, int32_t y)
|
||||
{
|
||||
auto& sdl_window = *static_cast<SDLWindow*>(window);
|
||||
SDL_SetWindowPosition(sdl_window.window, x, y);
|
||||
}
|
||||
|
||||
static void sdl3_poll_events(void*)
|
||||
{
|
||||
uint64_t dummy;
|
||||
read(s_eventfd, &dummy, sizeof(dummy));
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
if (auto it = s_window_map.find(event.window.windowID); it != s_window_map.end())
|
||||
on_window_close_event(it->value->wid);
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
||||
if (auto it = s_window_map.find(event.window.windowID); it != s_window_map.end())
|
||||
{
|
||||
auto& window = *it->value;
|
||||
window.width = event.window.data1;
|
||||
window.height = event.window.data2;
|
||||
|
||||
SDL_DestroyTexture(window.texture);
|
||||
window.texture = SDL_CreateTexture(window.renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, window.width, window.height);
|
||||
ASSERT(window.texture);
|
||||
|
||||
on_window_resize_event(window.wid, window.width, window.height);
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_MOVED:
|
||||
if (auto it = s_window_map.find(event.motion.windowID); it != s_window_map.end())
|
||||
on_window_move_event(it->value->wid, event.window.data1, event.window.data2);
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||
if (auto it = s_window_map.find(event.motion.windowID); it != s_window_map.end())
|
||||
on_window_focus_event(it->value->wid, (event.type == SDL_EVENT_WINDOW_FOCUS_GAINED));
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
|
||||
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
|
||||
if (auto it = s_window_map.find(event.motion.windowID); it != s_window_map.end())
|
||||
on_window_fullscreen_event(it->value->wid, (event.type == SDL_EVENT_WINDOW_ENTER_FULLSCREEN));
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
||||
if (auto it = s_window_map.find(event.motion.windowID); it != s_window_map.end())
|
||||
on_window_leave_event(it->value->wid);
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
if (auto it = s_window_map.find(event.motion.windowID); it != s_window_map.end())
|
||||
on_mouse_move_event(it->value->wid, event.motion.x, event.motion.y);
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
{
|
||||
uint8_t xbutton { 0 };
|
||||
switch (event.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT: xbutton = 1; break;
|
||||
case SDL_BUTTON_MIDDLE: xbutton = 2; break;
|
||||
case SDL_BUTTON_RIGHT: xbutton = 3; break;
|
||||
case SDL_BUTTON_X1: xbutton = 8; break;
|
||||
case SDL_BUTTON_X2: xbutton = 9; break;
|
||||
}
|
||||
|
||||
auto it = s_window_map.find(event.window.windowID);
|
||||
if (xbutton && it != s_window_map.end())
|
||||
on_mouse_button_event(it->value->wid, xbutton, (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN));
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
{
|
||||
uint8_t xbutton { 0 };
|
||||
if (event.wheel.y > 0)
|
||||
xbutton = 4;
|
||||
if (event.wheel.y < 0)
|
||||
xbutton = 5;
|
||||
|
||||
auto it = s_window_map.find(event.window.windowID);
|
||||
if (xbutton && it != s_window_map.end())
|
||||
{
|
||||
on_mouse_button_event(it->value->wid, xbutton, true);
|
||||
on_mouse_button_event(it->value->wid, xbutton, false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_KEY_UP:
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
{
|
||||
const uint8_t xmod =
|
||||
((event.key.mod & SDL_KMOD_SHIFT) ? (1 << 0) : 0) |
|
||||
((event.key.mod & SDL_KMOD_CAPS) ? (1 << 1) : 0) |
|
||||
((event.key.mod & SDL_KMOD_CTRL) ? (1 << 2) : 0) |
|
||||
((event.key.mod & SDL_KMOD_ALT) ? (1 << 3) : 0) |
|
||||
((event.key.mod & SDL_KMOD_NUM) ? (1 << 4) : 0) |
|
||||
((event.key.mod & SDL_KMOD_LEVEL5) ? (1 << 5) : 0) |
|
||||
((event.key.mod & SDL_KMOD_GUI) ? (1 << 6) : 0) |
|
||||
((event.key.mod & SDL_KMOD_MODE) ? (1 << 7) : 0);
|
||||
|
||||
auto it = s_window_map.find(event.window.windowID);
|
||||
if (it != s_window_map.end())
|
||||
on_key_event(it->value->wid, event.key.scancode, xmod, (event.type == SDL_EVENT_KEY_DOWN));
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_KEYMAP_CHANGED:
|
||||
sdl3_initialize_keymap();
|
||||
on_keymap_changed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sdl3_invalidate(PlatformWindow* window, const void* src_pixels, uint32_t src_pitch, uint32_t x, uint32_t y, uint32_t width, uint32_t height)
|
||||
{
|
||||
auto& sdl_window = *static_cast<SDLWindow*>(window);
|
||||
|
||||
ASSERT(x < sdl_window.width && width <= sdl_window.width - x);
|
||||
ASSERT(y < sdl_window.height && height <= sdl_window.height - y);
|
||||
|
||||
const SDL_Rect rect {
|
||||
.x = static_cast<int>(x),
|
||||
.y = static_cast<int>(y),
|
||||
.w = static_cast<int>(width),
|
||||
.h = static_cast<int>(height),
|
||||
};
|
||||
|
||||
void* dst_pixels;
|
||||
int dst_pitch;
|
||||
if (!SDL_LockTexture(sdl_window.texture, &rect, &dst_pixels, &dst_pitch))
|
||||
{
|
||||
dwarnln("Could not lock texture: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32_t y_off = 0; y_off < rect.h; y_off++)
|
||||
{
|
||||
memcpy(
|
||||
static_cast< uint8_t*>(dst_pixels) + y_off * dst_pitch,
|
||||
static_cast<const uint8_t*>(src_pixels) + y_off * src_pitch,
|
||||
rect.w * sizeof(uint32_t)
|
||||
);
|
||||
}
|
||||
|
||||
SDL_UnlockTexture(sdl_window.texture);
|
||||
}
|
||||
|
||||
static void sdl3_end_frame(PlatformWindow* window)
|
||||
{
|
||||
auto& sdl_window = *static_cast<SDLWindow*>(window);
|
||||
SDL_RenderClear(sdl_window.renderer);
|
||||
SDL_RenderTexture(sdl_window.renderer, sdl_window.texture, nullptr, nullptr);
|
||||
SDL_RenderPresent(sdl_window.renderer);
|
||||
}
|
||||
|
||||
static void sdl3_request_fullscreen(PlatformWindow* window, bool fullscreen)
|
||||
{
|
||||
auto& sdl_window = *static_cast<SDLWindow*>(window);
|
||||
SDL_SetWindowFullscreen(sdl_window.window, fullscreen);
|
||||
}
|
||||
|
||||
static void sdl3_warp_pointer(int32_t x, int32_t y, bool relative)
|
||||
{
|
||||
if (relative)
|
||||
{
|
||||
float cx, cy;
|
||||
SDL_GetGlobalMouseState(&cx, &cy);
|
||||
x += cx;
|
||||
x += cy;
|
||||
}
|
||||
|
||||
SDL_WarpMouseGlobal(x, y);
|
||||
}
|
||||
|
||||
static void sdl3_query_pointer(int32_t* x, int32_t* y)
|
||||
{
|
||||
float cx, cy;
|
||||
SDL_GetGlobalMouseState(&cx, &cy);
|
||||
*x = cx;
|
||||
*y = cy;
|
||||
}
|
||||
|
||||
static void sdl3_set_pointer_grab(PlatformWindow* window, bool grabbed)
|
||||
{
|
||||
auto& sdl_window = *static_cast<SDLWindow*>(window);
|
||||
SDL_SetWindowMouseGrab(sdl_window.window, grabbed);
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl3_create_system_cursor(SystemCursorType type)
|
||||
{
|
||||
static constexpr SDL_SystemCursor cursor_type_map[] {
|
||||
[static_cast<size_t>(SystemCursorType::Pointer)] = SDL_SYSTEM_CURSOR_DEFAULT,
|
||||
[static_cast<size_t>(SystemCursorType::Text)] = SDL_SYSTEM_CURSOR_TEXT,
|
||||
[static_cast<size_t>(SystemCursorType::Wait)] = SDL_SYSTEM_CURSOR_WAIT,
|
||||
[static_cast<size_t>(SystemCursorType::Hand)] = SDL_SYSTEM_CURSOR_POINTER,
|
||||
[static_cast<size_t>(SystemCursorType::Help)] = SDL_SYSTEM_CURSOR_DEFAULT, // :(
|
||||
[static_cast<size_t>(SystemCursorType::Move)] = SDL_SYSTEM_CURSOR_MOVE,
|
||||
[static_cast<size_t>(SystemCursorType::Forbidden)] = SDL_SYSTEM_CURSOR_NOT_ALLOWED,
|
||||
[static_cast<size_t>(SystemCursorType::ResizeN)] = SDL_SYSTEM_CURSOR_N_RESIZE,
|
||||
[static_cast<size_t>(SystemCursorType::ResizeE)] = SDL_SYSTEM_CURSOR_E_RESIZE,
|
||||
[static_cast<size_t>(SystemCursorType::ResizeS)] = SDL_SYSTEM_CURSOR_S_RESIZE,
|
||||
[static_cast<size_t>(SystemCursorType::ResizeW)] = SDL_SYSTEM_CURSOR_W_RESIZE,
|
||||
[static_cast<size_t>(SystemCursorType::ResizeNW)] = SDL_SYSTEM_CURSOR_NW_RESIZE,
|
||||
[static_cast<size_t>(SystemCursorType::ResizeNE)] = SDL_SYSTEM_CURSOR_NE_RESIZE,
|
||||
[static_cast<size_t>(SystemCursorType::ResizeSW)] = SDL_SYSTEM_CURSOR_SW_RESIZE,
|
||||
[static_cast<size_t>(SystemCursorType::ResizeSE)] = SDL_SYSTEM_CURSOR_SE_RESIZE,
|
||||
[static_cast<size_t>(SystemCursorType::ResizeVertical)] = SDL_SYSTEM_CURSOR_NS_RESIZE,
|
||||
[static_cast<size_t>(SystemCursorType::ResizeHorizontal)] = SDL_SYSTEM_CURSOR_EW_RESIZE,
|
||||
};
|
||||
|
||||
auto cursor = TRY(BAN::UniqPtr<SDLCursor>::create());
|
||||
|
||||
cursor->cursor = SDL_CreateSystemCursor(cursor_type_map[static_cast<size_t>(type)]);
|
||||
if (cursor->cursor == nullptr)
|
||||
{
|
||||
dwarnln("Could not create SDL system cursor: {}", SDL_GetError());
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl3_create_bitmap_cursor(const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y)
|
||||
{
|
||||
auto cursor = TRY(BAN::UniqPtr<SDLCursor>::create());
|
||||
|
||||
SDL_Surface* surface = SDL_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_ARGB8888, const_cast<uint32_t*>(pixels), width * 4);
|
||||
if (surface == nullptr)
|
||||
{
|
||||
dwarnln("Could not create SDL surface for cursor: {}", SDL_GetError());
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
origin_x = BAN::Math::clamp<int32_t>(origin_x, 0, width - 1);
|
||||
origin_y = BAN::Math::clamp<int32_t>(origin_y, 0, height - 1);
|
||||
cursor->cursor = SDL_CreateColorCursor(surface, origin_x, origin_y);
|
||||
|
||||
SDL_DestroySurface(surface);
|
||||
|
||||
if (cursor->cursor == nullptr)
|
||||
{
|
||||
dwarnln("Could not create SDL color cursor: {}", SDL_GetError());
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
|
||||
}
|
||||
|
||||
static void sdl3_set_cursor(PlatformWindow*, PlatformCursor* cursor)
|
||||
{
|
||||
if (cursor == nullptr)
|
||||
SDL_SetCursor(s_default_cursor);
|
||||
else
|
||||
{
|
||||
auto& sdl_cursor = *static_cast<SDLCursor*>(cursor);
|
||||
SDL_SetCursor(sdl_cursor.cursor);
|
||||
}
|
||||
}
|
||||
|
||||
PlatformOps g_platform_ops = {
|
||||
.initialize = sdl3_initialize,
|
||||
.poll_events = sdl3_poll_events,
|
||||
.create_window = sdl3_create_window,
|
||||
.invalidate = sdl3_invalidate,
|
||||
.begin_frame = nullptr,
|
||||
.end_frame = sdl3_end_frame,
|
||||
.request_resize = sdl3_request_resize,
|
||||
.request_reposition = sdl3_request_reposition,
|
||||
.request_fullscreen = sdl3_request_fullscreen,
|
||||
.warp_pointer = sdl3_warp_pointer,
|
||||
.query_pointer = sdl3_query_pointer,
|
||||
.set_pointer_grab = sdl3_set_pointer_grab,
|
||||
.create_system_cursor = sdl3_create_system_cursor,
|
||||
.create_bitmap_cursor = sdl3_create_bitmap_cursor,
|
||||
.set_cursor = sdl3_set_cursor,
|
||||
};
|
||||
|
||||
static uint32_t sdl3_keycode_to_x_keysym(SDL_Keycode keycode);
|
||||
|
||||
static void sdl3_initialize_keymap()
|
||||
{
|
||||
static constexpr SDL_Keymod modifier_map[] {
|
||||
SDL_KMOD_NONE,
|
||||
SDL_KMOD_SHIFT,
|
||||
SDL_KMOD_MODE,
|
||||
SDL_KMOD_MODE | SDL_KMOD_SHIFT,
|
||||
};
|
||||
|
||||
memset(g_keymap, 0, sizeof(g_keymap));
|
||||
memset(g_modifier_map, 0, sizeof(g_modifier_map));
|
||||
|
||||
for (size_t scancode = 0; scancode < g_keymap_size; scancode++)
|
||||
for (size_t layer = 0; layer < g_keymap_layers; layer++)
|
||||
if (const auto sdl_key = SDL_GetKeyFromScancode(static_cast<SDL_Scancode>(scancode), modifier_map[layer], false); sdl_key != SDLK_UNKNOWN)
|
||||
g_keymap[scancode][layer] = sdl3_keycode_to_x_keysym(sdl_key);
|
||||
|
||||
const auto get_scancode = [](SDL_Keycode keycode) -> uint8_t {
|
||||
const auto scancode = SDL_GetScancodeFromKey(keycode, nullptr);;
|
||||
if (scancode != SDL_SCANCODE_UNKNOWN && scancode < g_keymap_size)
|
||||
return scancode + g_keymap_min_keycode;
|
||||
return 0;
|
||||
};
|
||||
|
||||
g_modifier_map[0][0] = get_scancode(SDLK_LSHIFT);
|
||||
g_modifier_map[0][1] = get_scancode(SDLK_RSHIFT);
|
||||
|
||||
g_modifier_map[1][0] = get_scancode(SDLK_CAPSLOCK);
|
||||
|
||||
g_modifier_map[2][0] = get_scancode(SDLK_LCTRL);
|
||||
g_modifier_map[2][1] = get_scancode(SDLK_RCTRL);
|
||||
|
||||
g_modifier_map[3][0] = get_scancode(SDLK_LALT);
|
||||
g_modifier_map[3][1] = get_scancode(SDLK_RALT);
|
||||
|
||||
g_modifier_map[4][0] = get_scancode(SDLK_NUMLOCKCLEAR);
|
||||
|
||||
g_modifier_map[5][0] = get_scancode(SDLK_LEVEL5_SHIFT);
|
||||
|
||||
g_modifier_map[6][0] = get_scancode(SDLK_LGUI);
|
||||
g_modifier_map[6][1] = get_scancode(SDLK_RGUI);
|
||||
|
||||
g_modifier_map[7][0] = get_scancode(SDLK_MODE);
|
||||
}
|
||||
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/XF86keysym.h>
|
||||
|
||||
#include <wctype.h>
|
||||
|
||||
static uint32_t sdl3_keycode_to_x_keysym(SDL_Keycode keycode)
|
||||
{
|
||||
if (iswprint(keycode))
|
||||
{
|
||||
if (keycode >= 0x100)
|
||||
keycode |= 0x01000000;
|
||||
return keycode;
|
||||
}
|
||||
|
||||
switch (keycode)
|
||||
{
|
||||
case SDLK_RETURN: return XK_Return;
|
||||
case SDLK_ESCAPE: return XK_Escape;
|
||||
case SDLK_BACKSPACE: return XK_BackSpace;
|
||||
case SDLK_TAB: return XK_Tab;
|
||||
case SDLK_DELETE: return XK_Delete;
|
||||
case SDLK_CAPSLOCK: return XK_Caps_Lock;
|
||||
case SDLK_F1: return XK_F1;
|
||||
case SDLK_F2: return XK_F2;
|
||||
case SDLK_F3: return XK_F3;
|
||||
case SDLK_F4: return XK_F4;
|
||||
case SDLK_F5: return XK_F5;
|
||||
case SDLK_F6: return XK_F6;
|
||||
case SDLK_F7: return XK_F7;
|
||||
case SDLK_F8: return XK_F8;
|
||||
case SDLK_F9: return XK_F9;
|
||||
case SDLK_F10: return XK_F10;
|
||||
case SDLK_F11: return XK_F11;
|
||||
case SDLK_F12: return XK_F12;
|
||||
case SDLK_PRINTSCREEN: return XK_Print;
|
||||
case SDLK_SCROLLLOCK: return XK_Scroll_Lock;
|
||||
case SDLK_PAUSE: return XK_Pause;
|
||||
case SDLK_INSERT: return XK_Insert;
|
||||
case SDLK_HOME: return XK_Home;
|
||||
case SDLK_PAGEUP: return XK_Page_Up;
|
||||
case SDLK_END: return XK_End;
|
||||
case SDLK_PAGEDOWN: return XK_Page_Down;
|
||||
case SDLK_RIGHT: return XK_Right;
|
||||
case SDLK_LEFT: return XK_Left;
|
||||
case SDLK_DOWN: return XK_Down;
|
||||
case SDLK_UP: return XK_Up;
|
||||
case SDLK_NUMLOCKCLEAR: return XK_Num_Lock;
|
||||
case SDLK_KP_DIVIDE: return XK_KP_Divide;
|
||||
case SDLK_KP_MULTIPLY: return XK_KP_Multiply;
|
||||
case SDLK_KP_MINUS: return XK_KP_Subtract;
|
||||
case SDLK_KP_PLUS: return XK_KP_Add;
|
||||
case SDLK_KP_ENTER: return XK_KP_Enter;
|
||||
case SDLK_KP_1: return XK_KP_1;
|
||||
case SDLK_KP_2: return XK_KP_2;
|
||||
case SDLK_KP_3: return XK_KP_3;
|
||||
case SDLK_KP_4: return XK_KP_4;
|
||||
case SDLK_KP_5: return XK_KP_5;
|
||||
case SDLK_KP_6: return XK_KP_6;
|
||||
case SDLK_KP_7: return XK_KP_7;
|
||||
case SDLK_KP_8: return XK_KP_8;
|
||||
case SDLK_KP_9: return XK_KP_9;
|
||||
case SDLK_KP_0: return XK_KP_0;
|
||||
case SDLK_KP_PERIOD: return XK_KP_Decimal;
|
||||
case SDLK_APPLICATION: return XF86XK_ApplicationLeft;
|
||||
case SDLK_POWER: return XF86XK_PowerOff;
|
||||
case SDLK_KP_EQUALS: return XK_KP_Equal;
|
||||
case SDLK_F13: return XK_F13;
|
||||
case SDLK_F14: return XK_F14;
|
||||
case SDLK_F15: return XK_F15;
|
||||
case SDLK_F16: return XK_F16;
|
||||
case SDLK_F17: return XK_F17;
|
||||
case SDLK_F18: return XK_F18;
|
||||
case SDLK_F19: return XK_F19;
|
||||
case SDLK_F20: return XK_F20;
|
||||
case SDLK_F21: return XK_F21;
|
||||
case SDLK_F22: return XK_F22;
|
||||
case SDLK_F23: return XK_F23;
|
||||
case SDLK_F24: return XK_F24;
|
||||
case SDLK_EXECUTE: return XK_Execute;
|
||||
case SDLK_HELP: return XK_Help;
|
||||
case SDLK_MENU: return XK_Menu;
|
||||
case SDLK_SELECT: return XK_Select;
|
||||
case SDLK_STOP: return XF86XK_Stop;
|
||||
case SDLK_AGAIN: return XK_Redo;
|
||||
case SDLK_UNDO: return XK_Undo;
|
||||
case SDLK_CUT: return XF86XK_Cut;
|
||||
case SDLK_COPY: return XF86XK_Copy;
|
||||
case SDLK_PASTE: return XF86XK_Paste;
|
||||
case SDLK_FIND: return XK_Find;
|
||||
case SDLK_MUTE: return XF86XK_AudioMute;
|
||||
case SDLK_VOLUMEUP: return XF86XK_AudioRaiseVolume;
|
||||
case SDLK_VOLUMEDOWN: return XF86XK_AudioLowerVolume;
|
||||
case SDLK_KP_COMMA: return XK_KP_Separator;
|
||||
//case SDLK_KP_EQUALSAS400: return ;
|
||||
//case SDLK_ALTERASE: return ;
|
||||
case SDLK_SYSREQ: return XK_Sys_Req;
|
||||
case SDLK_CANCEL: return XK_Cancel;
|
||||
case SDLK_CLEAR: return XK_Clear;
|
||||
case SDLK_PRIOR: return XK_Prior;
|
||||
case SDLK_RETURN2: return XK_Return;
|
||||
//case SDLK_SEPARATOR: return ;
|
||||
//case SDLK_OUT: return ;
|
||||
//case SDLK_OPER: return ;
|
||||
//case SDLK_CLEARAGAIN: return ;
|
||||
//case SDLK_CRSEL: return ;
|
||||
//case SDLK_EXSEL: return ;
|
||||
//case SDLK_KP_00: return ;
|
||||
//case SDLK_KP_000: return ;
|
||||
//case SDLK_THOUSANDSSEPARATOR: return ;
|
||||
//case SDLK_DECIMALSEPARATOR: return ;
|
||||
//case SDLK_CURRENCYUNIT: return ;
|
||||
//case SDLK_CURRENCYSUBUNIT: return ;
|
||||
//case SDLK_KP_LEFTPAREN: return ;
|
||||
//case SDLK_KP_RIGHTPAREN: return ;
|
||||
//case SDLK_KP_LEFTBRACE: return ;
|
||||
//case SDLK_KP_RIGHTBRACE: return ;
|
||||
case SDLK_KP_TAB: return XK_KP_Tab;
|
||||
//case SDLK_KP_BACKSPACE: return ;
|
||||
//case SDLK_KP_A: return ;
|
||||
//case SDLK_KP_B: return ;
|
||||
//case SDLK_KP_C: return ;
|
||||
//case SDLK_KP_D: return ;
|
||||
//case SDLK_KP_E: return ;
|
||||
//case SDLK_KP_F: return ;
|
||||
//case SDLK_KP_XOR: return ;
|
||||
//case SDLK_KP_POWER: return ;
|
||||
//case SDLK_KP_PERCENT: return ;
|
||||
//case SDLK_KP_LESS: return ;
|
||||
//case SDLK_KP_GREATER: return ;
|
||||
//case SDLK_KP_AMPERSAND: return ;
|
||||
//case SDLK_KP_DBLAMPERSAND: return ;
|
||||
//case SDLK_KP_VERTICALBAR: return ;
|
||||
//case SDLK_KP_DBLVERTICALBAR: return ;
|
||||
//case SDLK_KP_COLON: return ;
|
||||
//case SDLK_KP_HASH: return ;
|
||||
//case SDLK_KP_SPACE: return ;
|
||||
//case SDLK_KP_AT: return ;
|
||||
//case SDLK_KP_EXCLAM: return ;
|
||||
//case SDLK_KP_MEMSTORE: return ;
|
||||
//case SDLK_KP_MEMRECALL: return ;
|
||||
//case SDLK_KP_MEMCLEAR: return ;
|
||||
//case SDLK_KP_MEMADD: return ;
|
||||
//case SDLK_KP_MEMSUBTRACT: return ;
|
||||
//case SDLK_KP_MEMMULTIPLY: return ;
|
||||
//case SDLK_KP_MEMDIVIDE: return ;
|
||||
//case SDLK_KP_PLUSMINUS: return ;
|
||||
//case SDLK_KP_CLEAR: return ;
|
||||
//case SDLK_KP_CLEARENTRY: return ;
|
||||
//case SDLK_KP_BINARY: return ;
|
||||
//case SDLK_KP_OCTAL: return ;
|
||||
//case SDLK_KP_DECIMAL: return ;
|
||||
//case SDLK_KP_HEXADECIMAL: return ;
|
||||
case SDLK_LCTRL: return XK_Control_L;
|
||||
case SDLK_LSHIFT: return XK_Shift_L;
|
||||
case SDLK_LALT: return XK_Alt_L;
|
||||
case SDLK_LGUI: return XK_Super_L;
|
||||
case SDLK_RCTRL: return XK_Control_R;
|
||||
case SDLK_RSHIFT: return XK_Shift_R;
|
||||
case SDLK_RALT: return XK_Alt_R;
|
||||
case SDLK_RGUI: return XK_Super_R;
|
||||
case SDLK_MODE: return XK_Mode_switch;
|
||||
case SDLK_SLEEP: return XF86XK_Sleep;
|
||||
case SDLK_WAKE: return XF86XK_WakeUp;
|
||||
//case SDLK_CHANNEL_INCREMENT: return XF86XK_ChannelUp;
|
||||
//case SDLK_CHANNEL_DECREMENT: return XF86XK_ChannelDown;
|
||||
case SDLK_MEDIA_PLAY: return XF86XK_AudioPlay;
|
||||
case SDLK_MEDIA_PAUSE: return XF86XK_AudioPause;
|
||||
case SDLK_MEDIA_RECORD: return XF86XK_AudioRecord;
|
||||
case SDLK_MEDIA_FAST_FORWARD: return XF86XK_AudioForward;
|
||||
case SDLK_MEDIA_REWIND: return XF86XK_AudioRewind;
|
||||
case SDLK_MEDIA_NEXT_TRACK: return XF86XK_AudioNext;
|
||||
case SDLK_MEDIA_PREVIOUS_TRACK: return XF86XK_AudioPrev;
|
||||
case SDLK_MEDIA_STOP: return XF86XK_AudioStop;
|
||||
case SDLK_MEDIA_EJECT: return XF86XK_Eject;
|
||||
//case SDLK_MEDIA_PLAY_PAUSE: return XF86XK_MediaPlayPause;
|
||||
case SDLK_MEDIA_SELECT: return XF86XK_AudioMedia;
|
||||
case SDLK_AC_NEW: return XF86XK_New;
|
||||
case SDLK_AC_OPEN: return XF86XK_Open;
|
||||
case SDLK_AC_CLOSE: return XF86XK_Close;
|
||||
//case SDLK_AC_EXIT: return XF86XK_Exit;
|
||||
case SDLK_AC_SAVE: return XF86XK_Save;
|
||||
case SDLK_AC_PRINT: return XK_Print;
|
||||
//case SDLK_AC_PROPERTIES: return ;
|
||||
case SDLK_AC_SEARCH: return XF86XK_Search;
|
||||
case SDLK_AC_HOME: return XF86XK_HomePage; // ?
|
||||
case SDLK_AC_BACK: return XF86XK_Back;
|
||||
case SDLK_AC_FORWARD: return XF86XK_Forward;
|
||||
case SDLK_AC_STOP: return XF86XK_Stop;
|
||||
case SDLK_AC_REFRESH: return XF86XK_Refresh;
|
||||
case SDLK_AC_BOOKMARKS: return XF86XK_Book; // ?
|
||||
//case SDLK_SOFTLEFT: return ;
|
||||
//case SDLK_SOFTRIGHT: return ;
|
||||
//case SDLK_CALL: return ;
|
||||
//case SDLK_ENDCALL: return ;
|
||||
case SDLK_LEFT_TAB: return XK_ISO_Left_Tab;
|
||||
case SDLK_LEVEL5_SHIFT: return XK_ISO_Level5_Shift;
|
||||
case SDLK_MULTI_KEY_COMPOSE: return XK_Multi_key;
|
||||
case SDLK_LMETA: return XK_Meta_L;
|
||||
case SDLK_RMETA: return XK_Meta_R;
|
||||
case SDLK_LHYPER: return XK_Hyper_L;
|
||||
case SDLK_RHYPER: return XK_Hyper_R;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -87,9 +87,10 @@ BAN::ErrorOr<DrawableInfo> get_drawable_info(Client& client_info, CARD32 drawabl
|
||||
case Object::Type::Window:
|
||||
{
|
||||
auto& window = drawable_it->value->object.get<Object::Window>();
|
||||
info.data_u32 = window.pixels.data();
|
||||
info.w = window.width;
|
||||
info.h = window.height;
|
||||
auto& texture = window.texture();
|
||||
info.data_u32 = texture.pixels().data();
|
||||
info.w = texture.width();
|
||||
info.h = texture.height();
|
||||
info.depth = window.depth;
|
||||
break;
|
||||
}
|
||||
@@ -108,13 +109,3 @@ BAN::ErrorOr<DrawableInfo> get_drawable_info(Client& client_info, CARD32 drawabl
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
PlatformCursor* get_cursor_safe(CURSOR cid)
|
||||
{
|
||||
auto it = g_objects.find(cid);
|
||||
if (it == g_objects.end())
|
||||
return nullptr;
|
||||
if (it->value->type != Object::Type::Cursor)
|
||||
return nullptr;
|
||||
return it->value->object.get<BAN::UniqPtr<PlatformCursor>>().ptr();
|
||||
}
|
||||
|
||||
@@ -13,5 +13,3 @@ BAN::ErrorOr<Object::Window&> get_window(Client& client_info, CARD32 wid, BYTE o
|
||||
BAN::ErrorOr<Object::Pixmap&> get_pixmap(Client& client_info, CARD32 pid, BYTE op_major, BYTE op_minor = 0);
|
||||
BAN::ErrorOr<Object::GraphicsContext&> get_gc(Client& client_info, CARD32 gc, BYTE op_major, BYTE op_minor = 0);
|
||||
BAN::ErrorOr<DrawableInfo> get_drawable_info(Client& client_info, CARD32 drawable, BYTE op_major, BYTE op_minor = 0);
|
||||
|
||||
PlatformCursor* get_cursor_safe(CURSOR cid);
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
using ATOM = uint32_t;
|
||||
using BITGRAVITY = uint32_t;
|
||||
using COLORMAP = uint32_t;
|
||||
using CURSOR = uint32_t;
|
||||
using PIXMAP = uint32_t;
|
||||
using VISUALID = uint32_t;
|
||||
using WINDOW = uint32_t;
|
||||
using WINGRAVITY = uint32_t;
|
||||
@@ -1,352 +0,0 @@
|
||||
#include "../Events.h"
|
||||
#include "../Keymap.h"
|
||||
#include "../Platform.h"
|
||||
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
struct BananWindow final : public PlatformWindow
|
||||
{
|
||||
~BananWindow()
|
||||
{
|
||||
if (window)
|
||||
unregister_event_fd(window->server_fd());
|
||||
}
|
||||
|
||||
BAN::UniqPtr<LibGUI::Window> window;
|
||||
};
|
||||
|
||||
struct BananCursor final : public PlatformCursor
|
||||
{
|
||||
BAN::Vector<uint32_t> pixels;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
int32_t origin_x;
|
||||
int32_t origin_y;
|
||||
};
|
||||
|
||||
static BAN::ErrorOr<void> bananos_initialize_keymap();
|
||||
|
||||
static bool bananos_initialize()
|
||||
{
|
||||
auto attributes = LibGUI::Window::default_attributes;
|
||||
attributes.shown = false;
|
||||
|
||||
auto dummy_or_error = LibGUI::Window::create(0, 0, ""_sv, attributes);
|
||||
if (dummy_or_error.is_error())
|
||||
{
|
||||
dwarnln("Could not get display size: {}", dummy_or_error.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
register_display(0, 0, dummy_or_error.value()->width(), dummy_or_error.value()->height());
|
||||
|
||||
if (auto ret = bananos_initialize_keymap(); ret.is_error())
|
||||
{
|
||||
dwarnln("Could not initialize keymap: {}", ret.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bananos_poll_events(void* window)
|
||||
{
|
||||
auto& banan_window = *static_cast<BananWindow*>(window);
|
||||
banan_window.window->poll_events();
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::UniqPtr<PlatformWindow>> bananos_create_window(WindowType type, WINDOW wid, int32_t x, int32_t y, uint32_t width, uint32_t height)
|
||||
{
|
||||
(void)type;
|
||||
|
||||
auto window = TRY(BAN::UniqPtr<BananWindow>::create());
|
||||
|
||||
auto attributes = LibGUI::Window::default_attributes;
|
||||
attributes.shown = true;
|
||||
attributes.title_bar = false;
|
||||
attributes.resizable = true;
|
||||
attributes.alpha_channel = true;
|
||||
|
||||
auto gui_window = TRY(LibGUI::Window::create(width, height, ""_sv, attributes));
|
||||
auto* winp = gui_window.ptr();
|
||||
|
||||
gui_window->set_close_window_event_callback([wid] {
|
||||
on_window_close_event(wid);
|
||||
});
|
||||
gui_window->set_resize_window_event_callback([wid, winp]() {
|
||||
on_window_resize_event(wid, winp->width(), winp->height());
|
||||
});
|
||||
gui_window->set_window_focus_event_callback([wid](auto event) {
|
||||
on_window_focus_event(wid, event.focused);
|
||||
});
|
||||
gui_window->set_window_fullscreen_event_callback([wid](auto event) {
|
||||
on_window_fullscreen_event(wid, event.fullscreen);
|
||||
});
|
||||
gui_window->set_mouse_move_event_callback([wid](auto event) {
|
||||
on_mouse_move_event(wid, event.x, event.y);
|
||||
});
|
||||
gui_window->set_mouse_button_event_callback([wid](auto event) {
|
||||
uint8_t xbutton = 0;
|
||||
switch (event.button)
|
||||
{
|
||||
case LibInput::MouseButton::Left: xbutton = 1; break;
|
||||
case LibInput::MouseButton::Middle: xbutton = 2; break;
|
||||
case LibInput::MouseButton::Right: xbutton = 3; break;
|
||||
case LibInput::MouseButton::Extra1: xbutton = 8; break;
|
||||
case LibInput::MouseButton::Extra2: xbutton = 9; break;
|
||||
}
|
||||
if (xbutton)
|
||||
on_mouse_button_event(wid, xbutton, event.pressed);
|
||||
});
|
||||
gui_window->set_mouse_scroll_event_callback([wid](auto event) {
|
||||
on_mouse_button_event(wid, event.scroll > 0 ? 4 : 5, true);
|
||||
on_mouse_button_event(wid, event.scroll > 0 ? 4 : 5, false);
|
||||
});
|
||||
gui_window->set_key_event_callback([wid](auto event) {
|
||||
const uint8_t xmod =
|
||||
(event.shift() ? (1 << 0) : 0) |
|
||||
(event.caps_lock() ? (1 << 1) : 0) |
|
||||
(event.ctrl() ? (1 << 2) : 0) |
|
||||
(event.alt() ? (1 << 3) : 0) |
|
||||
(event.num_lock() ? (1 << 4) : 0) |
|
||||
// level5 shift
|
||||
// super
|
||||
(event.ralt() ? (1 << 7) : 0); // FIXME: altgr
|
||||
on_key_event(wid, event.scancode, xmod, event.pressed());
|
||||
});
|
||||
|
||||
window->window = BAN::move(gui_window);
|
||||
|
||||
register_event_fd(window->window->server_fd(), window.ptr());
|
||||
|
||||
return BAN::UniqPtr<PlatformWindow>(BAN::move(window));
|
||||
}
|
||||
|
||||
static void bananos_request_resize(PlatformWindow* window, uint32_t width, uint32_t height)
|
||||
{
|
||||
auto& banan_window = *static_cast<BananWindow*>(window);
|
||||
banan_window.window->request_resize(width, height);
|
||||
}
|
||||
|
||||
static void bananos_invalidate(PlatformWindow* window, const void* src_pixels, uint32_t src_pitch, uint32_t x, uint32_t y, uint32_t width, uint32_t height)
|
||||
{
|
||||
auto& banan_window = *static_cast<BananWindow*>(window);
|
||||
|
||||
auto& texture = banan_window.window->texture();
|
||||
void* dst_pixels = &texture.pixels()[y * texture.width() + x];
|
||||
const uint32_t dst_pitch = texture.width() * sizeof(uint32_t);
|
||||
|
||||
for (uint32_t y_off = 0; y_off < height; y_off++)
|
||||
{
|
||||
memcpy(
|
||||
static_cast< uint8_t*>(dst_pixels) + y_off * dst_pitch,
|
||||
static_cast<const uint8_t*>(src_pixels) + y_off * src_pitch,
|
||||
width * sizeof(uint32_t)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void bananos_end_frame(PlatformWindow* window)
|
||||
{
|
||||
auto& banan_window = *static_cast<BananWindow*>(window);
|
||||
banan_window.window->invalidate();
|
||||
}
|
||||
|
||||
static void bananos_request_fullscreen(PlatformWindow* window, bool fullscreen)
|
||||
{
|
||||
auto& banan_window = *static_cast<BananWindow*>(window);
|
||||
banan_window.window->set_fullscreen(fullscreen);
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> bananos_create_bitmap_cursor(const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y)
|
||||
{
|
||||
auto cursor = TRY(BAN::UniqPtr<BananCursor>::create());
|
||||
cursor->width = width;
|
||||
cursor->height = height;
|
||||
cursor->origin_x = origin_x;
|
||||
cursor->origin_y = origin_y;
|
||||
|
||||
TRY(cursor->pixels.resize(width * height));
|
||||
memcpy(cursor->pixels.data(), pixels, cursor->pixels.size() * 4);
|
||||
|
||||
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
|
||||
}
|
||||
|
||||
static void bananos_set_cursor(PlatformWindow* window, PlatformCursor* cursor)
|
||||
{
|
||||
if (window == nullptr)
|
||||
return;
|
||||
|
||||
auto& banan_window = *static_cast<BananWindow*>(window);
|
||||
if (cursor == nullptr)
|
||||
banan_window.window->set_cursor(0, 0, {}, 0, 0);
|
||||
else
|
||||
{
|
||||
auto& banan_cursor = *static_cast<BananCursor*>(cursor);
|
||||
banan_window.window->set_cursor(banan_cursor.width, banan_cursor.height, banan_cursor.pixels.span(), banan_cursor.origin_x, banan_cursor.origin_y);
|
||||
}
|
||||
}
|
||||
|
||||
PlatformOps g_platform_ops = {
|
||||
.initialize = bananos_initialize,
|
||||
.poll_events = bananos_poll_events,
|
||||
.create_window = bananos_create_window,
|
||||
.invalidate = bananos_invalidate,
|
||||
.begin_frame = nullptr,
|
||||
.end_frame = bananos_end_frame,
|
||||
.request_resize = bananos_request_resize,
|
||||
.request_reposition = nullptr,
|
||||
.request_fullscreen = bananos_request_fullscreen,
|
||||
.warp_pointer = nullptr,
|
||||
.query_pointer = nullptr,
|
||||
.set_pointer_grab = nullptr,
|
||||
.create_system_cursor = nullptr,
|
||||
.create_bitmap_cursor = bananos_create_bitmap_cursor,
|
||||
.set_cursor = bananos_set_cursor,
|
||||
};
|
||||
|
||||
#include <LibInput/KeyboardLayout.h>
|
||||
#include <LibInput/KeyEvent.h>
|
||||
|
||||
static uint32_t bananos_keyevent_to_x_keysym(LibInput::KeyEvent event);
|
||||
|
||||
static BAN::ErrorOr<void> bananos_initialize_keymap()
|
||||
{
|
||||
static constexpr uint16_t modifier_map[] {
|
||||
0,
|
||||
LibInput::KeyEvent::LShift,
|
||||
LibInput::KeyEvent::RAlt,
|
||||
LibInput::KeyEvent::RAlt | LibInput::KeyEvent::LShift,
|
||||
};
|
||||
|
||||
// FIXME: get this from somewhere (gui command? enviroment? tmp file?)
|
||||
const auto keymap_path = "./us.keymap"_sv;
|
||||
TRY(LibInput::KeyboardLayout::initialize());
|
||||
auto& keyboard_layout = LibInput::KeyboardLayout::get();
|
||||
TRY(keyboard_layout.load_from_file(keymap_path));
|
||||
|
||||
for (uint8_t scancode = 0; scancode < g_keymap_size; scancode++)
|
||||
for (size_t layer = 0; layer < g_keymap_layers; layer++)
|
||||
if (const auto event = keyboard_layout.key_event_from_raw({ .modifier = modifier_map[layer], .keycode = scancode }); event.key != LibInput::Key::None)
|
||||
g_keymap[scancode][layer] = bananos_keyevent_to_x_keysym(event);
|
||||
|
||||
using LibInput::keycode_normal;
|
||||
using LibInput::keycode_numpad;
|
||||
|
||||
g_modifier_map[0][0] = keycode_normal(3, 0) + g_keymap_min_keycode; // lshift
|
||||
g_modifier_map[0][1] = keycode_normal(3, 12) + g_keymap_min_keycode; // rshift
|
||||
|
||||
g_modifier_map[1][0] = keycode_normal(2, 0) + g_keymap_min_keycode; // caps lock
|
||||
|
||||
g_modifier_map[2][0] = keycode_normal(4, 0) + g_keymap_min_keycode; // lctrl
|
||||
g_modifier_map[2][1] = keycode_normal(4, 6) + g_keymap_min_keycode; // rctrl
|
||||
|
||||
g_modifier_map[3][0] = keycode_normal(4, 2) + g_keymap_min_keycode; // lalt
|
||||
g_modifier_map[3][1] = keycode_normal(4, 5) + g_keymap_min_keycode; // ralt
|
||||
|
||||
g_modifier_map[4][0] = keycode_numpad(0, 0) + g_keymap_min_keycode; // num lock
|
||||
|
||||
//g_modifier_map[5][0] = level5 shift;
|
||||
|
||||
g_modifier_map[6][0] = keycode_normal(4, 1) + g_keymap_min_keycode; // lsuper
|
||||
g_modifier_map[6][1] = keycode_normal(4, 4) + g_keymap_min_keycode; // rsuper
|
||||
|
||||
g_modifier_map[7][0] = keycode_normal(4, 5) + g_keymap_min_keycode; // FIXME: altgr
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#include <BAN/UTF8.h>
|
||||
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/XF86keysym.h>
|
||||
|
||||
#include <wctype.h>
|
||||
|
||||
static uint32_t bananos_keyevent_to_x_keysym(LibInput::KeyEvent event)
|
||||
{
|
||||
using namespace LibInput;
|
||||
|
||||
if (const char* utf8 = key_to_utf8(event.key, event.modifier))
|
||||
{
|
||||
uint32_t codepoint = BAN::UTF8::to_codepoint(utf8);
|
||||
if (codepoint != BAN::UTF8::invalid && iswprint(codepoint))
|
||||
{
|
||||
if (codepoint >= 0x100)
|
||||
codepoint |= 0x01000000;
|
||||
return codepoint;
|
||||
}
|
||||
}
|
||||
|
||||
switch (event.key)
|
||||
{
|
||||
case Key::F1: return XK_F1;
|
||||
case Key::F2: return XK_F2;
|
||||
case Key::F3: return XK_F3;
|
||||
case Key::F4: return XK_F4;
|
||||
case Key::F5: return XK_F5;
|
||||
case Key::F6: return XK_F6;
|
||||
case Key::F7: return XK_F7;
|
||||
case Key::F8: return XK_F8;
|
||||
case Key::F9: return XK_F9;
|
||||
case Key::F10: return XK_F10;
|
||||
case Key::F11: return XK_F11;
|
||||
case Key::F12: return XK_F12;
|
||||
case Key::Insert: return XK_Insert;
|
||||
case Key::PrintScreen: return XK_Print;
|
||||
case Key::Delete: return XK_Delete;
|
||||
case Key::Home: return XK_Home;
|
||||
case Key::End: return XK_End;
|
||||
case Key::PageUp: return XK_Page_Up;
|
||||
case Key::PageDown: return XK_Page_Down;
|
||||
case Key::Enter: return XK_Return;
|
||||
case Key::Backspace: return XK_BackSpace;
|
||||
case Key::Escape: return XK_Escape;
|
||||
case Key::Tab: return XK_Tab;
|
||||
case Key::CapsLock: return XK_Caps_Lock;
|
||||
case Key::LeftShift: return XK_Shift_L;
|
||||
case Key::LeftCtrl: return XK_Control_L;
|
||||
case Key::Super: return XK_Super_L;
|
||||
case Key::LeftAlt: return XK_Alt_L;
|
||||
case Key::RightAlt: return XK_Alt_R;
|
||||
case Key::RightCtrl: return XK_Control_R;
|
||||
case Key::RightShift: return XK_Shift_R;
|
||||
case Key::ArrowUp: return XK_Up;
|
||||
case Key::ArrowDown: return XK_Down;
|
||||
case Key::ArrowLeft: return XK_Left;
|
||||
case Key::ArrowRight: return XK_Right;
|
||||
case Key::NumLock: return XK_Num_Lock;
|
||||
case Key::ScrollLock: return XK_Scroll_Lock;
|
||||
case Key::Numpad0: return XK_KP_0;
|
||||
case Key::Numpad1: return XK_KP_1;
|
||||
case Key::Numpad2: return XK_KP_2;
|
||||
case Key::Numpad3: return XK_KP_3;
|
||||
case Key::Numpad4: return XK_KP_4;
|
||||
case Key::Numpad5: return XK_KP_5;
|
||||
case Key::Numpad6: return XK_KP_6;
|
||||
case Key::Numpad7: return XK_KP_7;
|
||||
case Key::Numpad8: return XK_KP_8;
|
||||
case Key::Numpad9: return XK_KP_9;
|
||||
case Key::NumpadPlus: return XK_KP_Add;
|
||||
case Key::NumpadMinus: return XK_KP_Subtract;
|
||||
case Key::NumpadMultiply: return XK_KP_Multiply;
|
||||
case Key::NumpadDivide: return XK_KP_Divide;
|
||||
case Key::NumpadEnter: return XK_KP_Enter;
|
||||
case Key::NumpadDecimal: return XK_KP_Decimal;
|
||||
case Key::VolumeMute: return XF86XK_AudioMute;
|
||||
case Key::VolumeUp: return XF86XK_AudioRaiseVolume;
|
||||
case Key::VolumeDown: return XF86XK_AudioLowerVolume;
|
||||
case Key::Calculator: return XF86XK_Calculator;
|
||||
case Key::MediaPlayPause: return XF86XK_AudioPlay;
|
||||
case Key::MediaStop: return XF86XK_AudioStop;
|
||||
case Key::MediaPrevious: return XF86XK_AudioPrev;
|
||||
case Key::MediaNext: return XF86XK_AudioNext;
|
||||
default: break;
|
||||
}
|
||||
|
||||
static_assert(static_cast<size_t>(Key::Count) == 145, "update keymap");
|
||||
|
||||
return 0;
|
||||
}
|
||||
187
xbanan/main.cpp
187
xbanan/main.cpp
@@ -1,17 +1,16 @@
|
||||
#include "Base.h"
|
||||
#include "Definitions.h"
|
||||
#include "Keymap.h"
|
||||
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define USE_UNIX_SOCKET 1
|
||||
#define USE_UNIX_SOCKET 0
|
||||
|
||||
#if USE_UNIX_SOCKET
|
||||
#include <sys/un.h>
|
||||
@@ -19,7 +18,18 @@
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
CARD32 g_next_global_id { 1 };
|
||||
static const xRectangle s_screen_bounds =
|
||||
[]() {
|
||||
auto attributes = LibGUI::Window::default_attributes;
|
||||
attributes.shown = false;
|
||||
auto window = MUST(LibGUI::Window::create(0, 0, ""_sv, attributes));
|
||||
return xRectangle {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = static_cast<CARD16>(window->width()),
|
||||
.height = static_cast<CARD16>(window->height()),
|
||||
};
|
||||
}();
|
||||
|
||||
const xPixmapFormat g_formats[6] {
|
||||
{
|
||||
@@ -60,7 +70,7 @@ const xDepth g_depth {
|
||||
};
|
||||
|
||||
const xVisualType g_visual {
|
||||
.visualID = g_next_global_id++,
|
||||
.visualID = 1,
|
||||
.c_class = TrueColor,
|
||||
.bitsPerRGB = 8,
|
||||
.colormapEntries = 256,
|
||||
@@ -69,27 +79,25 @@ const xVisualType g_visual {
|
||||
.blueMask = 0x0000FF,
|
||||
};
|
||||
|
||||
xWindowRoot g_root {
|
||||
.windowId = g_next_global_id++,
|
||||
const xWindowRoot g_root {
|
||||
.windowId = 2,
|
||||
.defaultColormap = 0,
|
||||
.whitePixel = 0xFFFFFF,
|
||||
.blackPixel = 0x000000,
|
||||
.currentInputMask = 0,
|
||||
.pixWidth = 0,
|
||||
.pixHeight = 0,
|
||||
.mmWidth = 0,
|
||||
.mmHeight = 0,
|
||||
.pixWidth = s_screen_bounds.width,
|
||||
.pixHeight = s_screen_bounds.height,
|
||||
.mmWidth = static_cast<CARD16>(s_screen_bounds.width * 254 / 960), // 96 DPI
|
||||
.mmHeight = static_cast<CARD16>(s_screen_bounds.height * 254 / 960), // 96 DPI
|
||||
.minInstalledMaps = 1,
|
||||
.maxInstalledMaps = 1,
|
||||
.rootVisualID = g_visual.visualID,
|
||||
.backingStore = 0,
|
||||
.saveUnders = 0,
|
||||
.rootDepth = g_depth.depth,
|
||||
.rootDepth = 24,
|
||||
.nDepths = 1,
|
||||
};
|
||||
|
||||
BAN::Vector<DisplayInfo> g_displays;
|
||||
|
||||
BAN::HashMap<CARD32, BAN::UniqPtr<Object>> g_objects;
|
||||
|
||||
BAN::HashMap<BAN::String, ATOM> g_atoms_name_to_id;
|
||||
@@ -103,8 +111,6 @@ int g_server_grabber_fd = -1;
|
||||
|
||||
int main()
|
||||
{
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
for (int sig = 1; sig < NSIG; sig++)
|
||||
if (sig != SIGWINCH)
|
||||
signal(sig, exit);
|
||||
@@ -166,13 +172,11 @@ int main()
|
||||
return 1;
|
||||
}
|
||||
|
||||
epoll_event event { .events = EPOLLIN, .data = { .ptr = nullptr } };
|
||||
if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, server_sock, &event) == -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;
|
||||
}
|
||||
perror("xbanan: epoll_ctl");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define APPEND_ATOM(name) do { \
|
||||
@@ -258,60 +262,9 @@ int main()
|
||||
APPEND_ATOM_CUSTOM(WM_DELETE_WINDOW);
|
||||
APPEND_ATOM_CUSTOM(_NET_WM_STATE);
|
||||
APPEND_ATOM_CUSTOM(_NET_WM_STATE_FULLSCREEN);
|
||||
APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE);
|
||||
APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE_DIALOG);
|
||||
APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE_NORMAL);
|
||||
APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE_SPLASH);
|
||||
APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE_UTILITY);
|
||||
#undef APPEND_ATOM_CUSTOM
|
||||
|
||||
if (!g_platform_ops.initialize())
|
||||
return 1;
|
||||
|
||||
if (g_displays.empty())
|
||||
{
|
||||
dwarnln("No displays windows initilized");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t display_min_x { INT32_MAX }, display_min_y { INT32_MAX };
|
||||
int32_t display_max_x { INT32_MIN }, display_max_y { INT32_MIN };
|
||||
for (const auto& display : g_displays)
|
||||
{
|
||||
display_min_x = BAN::Math::min<int32_t>(display_min_x, display.x);
|
||||
display_min_y = BAN::Math::min<int32_t>(display_min_y, display.y);
|
||||
display_max_x = BAN::Math::max<int32_t>(display_max_x, display.x + display.w);
|
||||
display_max_y = BAN::Math::max<int32_t>(display_max_y, display.y + display.h);
|
||||
}
|
||||
|
||||
g_root.pixWidth = display_max_x - display_min_x;
|
||||
g_root.pixHeight = display_max_y - display_min_y;
|
||||
|
||||
g_root.mmWidth = g_root.pixWidth * 254 / 960; // 96 DPI
|
||||
g_root.mmHeight = g_root.pixHeight * 254 / 960; // 96 DPI
|
||||
|
||||
Client dummy_owner;
|
||||
MUST(g_objects.insert(g_root.windowId, MUST(BAN::UniqPtr<Object>::create(Object {
|
||||
.type = Object::Type::Window,
|
||||
.object = Object::Window {
|
||||
.owner = dummy_owner,
|
||||
.depth = g_root.rootDepth,
|
||||
.x = display_min_x,
|
||||
.y = display_min_y,
|
||||
.parent = None,
|
||||
.cursor = None,
|
||||
.c_class = InputOutput,
|
||||
.width = g_root.pixWidth,
|
||||
.height = g_root.pixHeight,
|
||||
.background = None,
|
||||
}
|
||||
}))));
|
||||
|
||||
MUST(g_objects.insert(g_visual.visualID,
|
||||
MUST(BAN::UniqPtr<Object>::create(Object {
|
||||
.type = Object::Type::Visual,
|
||||
}))
|
||||
));
|
||||
MUST(initialize_keymap());
|
||||
|
||||
printf("xbanan started\n");
|
||||
|
||||
@@ -334,18 +287,6 @@ int main()
|
||||
window.event_masks.remove(&client_info);
|
||||
}
|
||||
|
||||
for (auto it = client_info.objects.begin(); it != client_info.objects.end();)
|
||||
{
|
||||
auto obj_it = g_objects.find(*it);
|
||||
if (obj_it == g_objects.end() || obj_it->value->type != Object::Type::Window)
|
||||
it++;
|
||||
else
|
||||
{
|
||||
(void)destroy_window(client_info, *it);
|
||||
it = client_info.objects.begin();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto id : client_info.objects)
|
||||
{
|
||||
auto it = g_objects.find(id);
|
||||
@@ -353,28 +294,28 @@ int main()
|
||||
continue;
|
||||
|
||||
auto& object = *it->value;
|
||||
switch (object.type)
|
||||
if (object.type == Object::Type::Window)
|
||||
{
|
||||
case Object::Type::Visual:
|
||||
case Object::Type::Cursor:
|
||||
case Object::Type::Pixmap:
|
||||
case Object::Type::GraphicsContext:
|
||||
case Object::Type::Font:
|
||||
break;
|
||||
case Object::Type::Window:
|
||||
ASSERT_NOT_REACHED();
|
||||
case Object::Type::Extension:
|
||||
auto& extension = object.object.get<Object::Extension>();
|
||||
extension.destructor(extension);
|
||||
break;
|
||||
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());
|
||||
}
|
||||
}
|
||||
if (object.type == Object::Type::Extension)
|
||||
{
|
||||
auto& extension = object.object.get<Object::Extension>();
|
||||
extension.destructor(extension);
|
||||
}
|
||||
|
||||
g_objects.remove(it);
|
||||
}
|
||||
|
||||
epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
|
||||
g_epoll_thingies.remove(client_fd);
|
||||
close(client_fd);
|
||||
g_epoll_thingies.remove(client_fd);
|
||||
|
||||
if (client_fd == g_server_grabber_fd)
|
||||
{
|
||||
@@ -397,6 +338,25 @@ int main()
|
||||
}
|
||||
};
|
||||
|
||||
MUST(g_objects.insert(g_root.windowId,
|
||||
MUST(BAN::UniqPtr<Object>::create(Object {
|
||||
.type = Object::Type::Window,
|
||||
.object = Object::Window {
|
||||
.mapped = true,
|
||||
.depth = g_root.rootDepth,
|
||||
.parent = None,
|
||||
.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];
|
||||
@@ -413,21 +373,11 @@ int main()
|
||||
continue;
|
||||
}
|
||||
|
||||
BAN::Optional<uint32_t> client_pid;
|
||||
|
||||
#ifdef SO_PEERCRED
|
||||
ucred client_cred;
|
||||
socklen_t client_cred_len = sizeof(client_cred);
|
||||
if (getsockopt(client_sock, SOL_SOCKET, SO_PEERCRED, &client_cred, &client_cred_len) == 0)
|
||||
client_pid = client_cred.pid;
|
||||
#endif
|
||||
|
||||
MUST(g_epoll_thingies.insert(client_sock, {
|
||||
.type = EpollThingy::Type::Client,
|
||||
.value = Client {
|
||||
.fd = client_sock,
|
||||
.state = Client::State::ConnectionSetup,
|
||||
.pid = client_pid,
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -452,9 +402,10 @@ int main()
|
||||
continue;
|
||||
auto& epoll_thingy = it->value;
|
||||
|
||||
if (epoll_thingy.type == EpollThingy::Type::Event)
|
||||
if (epoll_thingy.type == EpollThingy::Type::Window)
|
||||
{
|
||||
g_platform_ops.poll_events(epoll_thingy.value.get<void*>());
|
||||
auto* window = epoll_thingy.value.get<LibGUI::Window*>();
|
||||
window->poll_events();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -629,18 +580,6 @@ int main()
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& [_, object] : g_objects)
|
||||
{
|
||||
if (object->type != Object::Type::Window)
|
||||
continue;
|
||||
auto& window = object->object.get<Object::Window>();
|
||||
if (!window.platform_window_invalidated)
|
||||
continue;
|
||||
if (g_platform_ops.end_frame)
|
||||
g_platform_ops.end_frame(window.platform_window.ptr());
|
||||
window.platform_window_invalidated = false;
|
||||
}
|
||||
|
||||
iterator_invalidated:
|
||||
for (auto& [_, thingy] : g_epoll_thingies)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user