diff --git a/xbanan/Base.cpp b/xbanan/Base.cpp index ad9a350..fce9fcc 100644 --- a/xbanan/Base.cpp +++ b/xbanan/Base.cpp @@ -159,6 +159,16 @@ static const char* s_opcode_to_name[] { [X_NoOperation] = "X_NoOperation", }; +void register_display(int32_t x, int32_t y, uint32_t width, uint32_t height) +{ + MUST(g_displays.push_back({ + .x = x, + .y = y, + .w = width, + .h = height, + })); +} + uint32_t Object::Window::full_event_mask() const { uint32_t full_event_mask = 0; @@ -200,9 +210,9 @@ BAN::ErrorOr setup_client_conneciton(Client& client_info, const xConnClien xConnSetupPrefix setup_prefix { .success = 1, .lengthReason = 0, // wtf is this - .majorVersion = client_prefix.majorVersion, - .minorVersion = client_prefix.minorVersion, - .length = 8 + 2*format_count + (8 + 0 + sz_xWindowRoot + sz_xDepth + sz_xVisualType) / 4, + .majorVersion = 11, + .minorVersion = 0, + .length = 8 + 2 * format_count + (8 + 0 + sz_xWindowRoot + sz_xDepth + sz_xVisualType) / 4, }; TRY(encode(client_info.output_buffer, setup_prefix)); @@ -1363,15 +1373,7 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) CARD16 width, height; CARD8 depth; - if (drawable_id == g_root.windowId) - { - width = g_root.pixWidth; - height = g_root.pixHeight; - depth = g_root.rootDepth; - x = 0; - y = 0; - } - else if (drawable.type == Object::Type::Window) + if (drawable.type == Object::Type::Window) { const auto& window = drawable.object.get(); width = window.width; diff --git a/xbanan/Definitions.h b/xbanan/Definitions.h index fd2099c..6562778 100644 --- a/xbanan/Definitions.h +++ b/xbanan/Definitions.h @@ -161,6 +161,12 @@ struct EpollThingy BAN::Variant value; }; +struct DisplayInfo +{ + int32_t x, y; + uint32_t w, h; +}; + extern const xPixmapFormat g_formats[6]; extern const xDepth g_depth; extern const xVisualType g_visual; @@ -176,3 +182,7 @@ extern int g_epoll_fd; extern BAN::HashMap g_epoll_thingies; extern int g_server_grabber_fd; + +extern BAN::Vector g_displays; + +extern CARD32 g_next_global_id; diff --git a/xbanan/ExtRANDR.cpp b/xbanan/ExtRANDR.cpp index 646e32e..af51d48 100644 --- a/xbanan/ExtRANDR.cpp +++ b/xbanan/ExtRANDR.cpp @@ -6,12 +6,64 @@ #include +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 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 extension_randr(Client& client_info, BAN::ConstByteSpan packet) { - static CARD32 crtc_id = 5; - static CARD32 output_id = 6; - static CARD32 mode_id = 7; - static CARD32 timestamp = time(nullptr); + if (s_randr_displays.empty()) + initialize_displays(); static xRenderTransform transform { 1 << 16, 0, 0, @@ -61,14 +113,14 @@ static BAN::ErrorOr extension_randr(Client& client_info, BAN::ConstByteSpa .setOfRotations = RR_Rotate_0, .sequenceNumber = client_info.sequence, .length = 3, - .root = g_root.windowId, - .timestamp = timestamp, - .configTimestamp = timestamp, - .nSizes = 1, - .sizeID = 0, - .rotation = RR_Rotate_0, - .rate = 60, - .nrateEnts = 1, + .root = g_root.windowId, + .timestamp = s_timestamp, + .configTimestamp = s_timestamp, + .nSizes = 1, + .sizeID = 0, + .rotation = RR_Rotate_0, + .rate = 60, + .nrateEnts = 1, }; TRY(encode(client_info.output_buffer, reply)); @@ -115,55 +167,63 @@ static BAN::ErrorOr extension_randr(Client& client_info, BAN::ConstByteSpa dprintln("RRGetScreenResources{}", current ? "Current" : ""); dprintln(" window: {}", request.window); - const auto mode_name = TRY(BAN::String::formatted("{}x{}", g_root.pixWidth, g_root.pixHeight)); + size_t mode_name_bytes = 0; + for (const auto& display : s_randr_displays) + mode_name_bytes += display.mode_str.size(); xRRGetScreenResourcesReply reply { .type = X_Reply, .sequenceNumber = client_info.sequence, - .length = static_cast(1 + 1 + 8 + (mode_name.size() + 3) / 4), - .timestamp = timestamp, - .configTimestamp = timestamp, - .nCrtcs = 1, - .nOutputs = 1, - .nModes = 1, - .nbytesNames = static_cast(mode_name.size()), + .length = static_cast(s_randr_displays.size() * (1 + 1 + 8) + (mode_name_bytes + 3) / 4), + .timestamp = s_timestamp, + .configTimestamp = s_timestamp, + .nCrtcs = static_cast(s_randr_displays.size()), + .nOutputs = static_cast(s_randr_displays.size()), + .nModes = static_cast(s_randr_displays.size()), + .nbytesNames = static_cast(mode_name_bytes), }; TRY(encode(client_info.output_buffer, reply)); - TRY(encode(client_info.output_buffer, crtc_id)); + for (const auto& display : s_randr_displays) + TRY(encode(client_info.output_buffer, display.crtc_id)); - TRY(encode(client_info.output_buffer, output_id)); + for (const auto& display : s_randr_displays) + TRY(encode(client_info.output_buffer, display.output_id)); - const CARD16 hsync_start = g_root.pixWidth; - const CARD16 hsync_end = g_root.pixWidth + 1; - const CARD16 htotal = g_root.pixWidth + 1; + 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 vsync_start = g_root.pixHeight; - const CARD16 vsync_end = g_root.pixHeight + 1; - const CARD16 vtotal = g_root.pixHeight + 1; + const CARD16 vsync_start = display.info.h; + const CARD16 vsync_end = display.info.h + 1; + const CARD16 vtotal = display.info.h + 1; - const CARD32 clock = htotal * vtotal * 60; + const CARD32 clock = htotal * vtotal * 60; - 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(mode_name.size()), - .modeFlags = RR_HSyncPositive | RR_VSyncPositive, - }; - TRY(encode(client_info.output_buffer, mode_info)); + xRRModeInfo mode_info { + .id = display.mode_id, + .width = static_cast(display.info.w), + .height = static_cast(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(display.mode_str.size()), + .modeFlags = RR_HSyncPositive | RR_VSyncPositive, + }; + TRY(encode(client_info.output_buffer, mode_info)); + } - 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')); + 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(client_info.output_buffer, 0)); break; } @@ -175,27 +235,31 @@ static BAN::ErrorOr 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 = 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, + .type = X_Reply, + .status = Success, + .sequenceNumber = client_info.sequence, + .length = static_cast(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(display.output_str.size()), }; TRY(encode(client_info.output_buffer, reply)); - 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)); + 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(client_info.output_buffer, 0)); break; } @@ -250,25 +314,27 @@ static BAN::ErrorOr 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 = timestamp, - .x = 0, - .y = 0, - .width = g_root.pixWidth, - .height = g_root.pixHeight, - .mode = mode_id, + .timestamp = s_timestamp, + .x = static_cast(display.info.x), + .y = static_cast(display.info.y), + .width = static_cast(display.info.w), + .height = static_cast(display.info.h), + .mode = display.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, output_id)); - TRY(encode(client_info.output_buffer, output_id)); + TRY(encode(client_info.output_buffer, display.output_id)); + TRY(encode(client_info.output_buffer, display.output_id)); break; } @@ -285,13 +351,13 @@ static BAN::ErrorOr extension_randr(Client& client_info, BAN::ConstByteSpa dprintln(" mode: {}", request.mode); dprintln(" rotation: {}", request.rotation); - timestamp = time(nullptr); + s_timestamp = time(nullptr); xRRSetCrtcConfigReply reply { .type = X_Reply, .status = Success, .sequenceNumber = client_info.sequence, .length = 0, - .newTimestamp = timestamp, + .newTimestamp = s_timestamp, }; TRY(encode(client_info.output_buffer, reply)); @@ -301,15 +367,15 @@ static BAN::ErrorOr extension_randr(Client& client_info, BAN::ConstByteSpa { auto request = decode(packet).value(); - dprintln("RRGetCrtcGammaSize"); - dprintln(" crtc: {}", request.crtc); + dwarnln("RRGetCrtcGammaSize"); + dwarnln(" crtc: {}", request.crtc); xRRGetCrtcGammaSizeReply reply { - .type = X_Reply, - .status = Success, - .sequenceNumber = client_info.sequence, - .length = 0, - .size = 1, + .type = X_Reply, + .status = Success, + .sequenceNumber = client_info.sequence, + .length = 0, + .size = 256, }; TRY(encode(client_info.output_buffer, reply)); @@ -323,17 +389,16 @@ static BAN::ErrorOr extension_randr(Client& client_info, BAN::ConstByteSpa dprintln(" crtc: {}", request.crtc); xRRGetCrtcGammaReply reply { - .type = X_Reply, - .status = Success, - .sequenceNumber = client_info.sequence, - .length = 2, - .size = 1, + .type = X_Reply, + .status = Success, + .sequenceNumber = client_info.sequence, + .length = (6 * 256) / 4, + .size = 256, }; TRY(encode(client_info.output_buffer, reply)); - TRY(encode(client_info.output_buffer, 1)); - TRY(encode(client_info.output_buffer, 1)); - TRY(encode(client_info.output_buffer, 1)); - TRY(encode(client_info.output_buffer, 0)); + for (size_t i = 0; i < 3; i++) + for (size_t j = 0; j < 256; j++) + TRY(encode(client_info.output_buffer, j * 65535 / 255)); break; } @@ -345,17 +410,17 @@ static BAN::ErrorOr 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)); @@ -369,23 +434,23 @@ static BAN::ErrorOr 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 = 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 = 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, }; TRY(encode(client_info.output_buffer, reply)); @@ -399,10 +464,10 @@ static BAN::ErrorOr extension_randr(Client& client_info, BAN::ConstByteSpa dprintln(" window: {}", request.window); xRRGetOutputPrimaryReply reply { - .type = X_Reply, - .sequenceNumber = client_info.sequence, - .length = 0, - .output = output_id, + .type = X_Reply, + .sequenceNumber = client_info.sequence, + .length = 0, + .output = s_randr_displays.front().output_id, }; TRY(encode(client_info.output_buffer, reply)); @@ -416,10 +481,10 @@ static BAN::ErrorOr extension_randr(Client& client_info, BAN::ConstByteSpa dprintln(" window: {}", request.window); xRRGetProvidersReply reply { - .type = X_Reply, - .sequenceNumber = client_info.sequence, - .length = 0, - .timestamp = timestamp, + .type = X_Reply, + .sequenceNumber = client_info.sequence, + .length = 0, + .timestamp = s_timestamp, .nProviders = 0, }; TRY(encode(client_info.output_buffer, reply)); @@ -435,31 +500,33 @@ static BAN::ErrorOr 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 = 6 + 1, - .timestamp = timestamp, - .nmonitors = 1, - .noutputs = 1, + .sequenceNumber = client_info.sequence, + .length = static_cast(6 * s_randr_displays.size() + s_randr_displays.size()), + .timestamp = s_timestamp, + .nmonitors = static_cast(s_randr_displays.size()), + .noutputs = static_cast(s_randr_displays.size()), }; TRY(encode(client_info.output_buffer, reply)); - 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)); + 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(display.info.x), + .y = static_cast(display.info.y), + .width = static_cast(display.info.w), + .height = static_cast(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)); + } break; } diff --git a/xbanan/Platform.h b/xbanan/Platform.h index 258ffba..ed29d1a 100644 --- a/xbanan/Platform.h +++ b/xbanan/Platform.h @@ -47,7 +47,7 @@ enum class SystemCursorType struct PlatformOps { /* Do platform initialization */ - bool (*initialize)(uint32_t* width, uint32_t* height); + bool (*initialize)(); /* Handle pending events */ void (*poll_events)(void*); /* Create a window with given size */ @@ -74,3 +74,5 @@ struct PlatformOps 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); diff --git a/xbanan/SDL3/sdl3.cpp b/xbanan/SDL3/sdl3.cpp index 6938406..41739ff 100644 --- a/xbanan/SDL3/sdl3.cpp +++ b/xbanan/SDL3/sdl3.cpp @@ -69,7 +69,7 @@ static void* sdl3_thread(void*) static void sdl3_initialize_keymap(); -static bool sdl3_initialize(uint32_t* display_w, uint32_t* display_h) +static bool sdl3_initialize() { if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { @@ -77,15 +77,16 @@ static bool sdl3_initialize(uint32_t* display_w, uint32_t* display_h) return false; } - *display_w = *display_h = 0; - const SDL_DisplayID* display_ids = SDL_GetDisplays(nullptr); for (int i = 0; display_ids[i]; i++) { SDL_Rect rect; - SDL_GetDisplayBounds(display_ids[i], &rect); - *display_w = BAN::Math::max(*display_w, rect.x + rect.w); - *display_h = BAN::Math::max(*display_h, rect.y + rect.h); + 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(); diff --git a/xbanan/banan-os/banan-os.cpp b/xbanan/banan-os/banan-os.cpp index 8d5060c..047bcd9 100644 --- a/xbanan/banan-os/banan-os.cpp +++ b/xbanan/banan-os/banan-os.cpp @@ -28,7 +28,7 @@ struct BananCursor final : public PlatformCursor static BAN::ErrorOr bananos_initialize_keymap(); -static bool bananos_initialize(uint32_t* display_w, uint32_t* display_h) +static bool bananos_initialize() { auto attributes = LibGUI::Window::default_attributes; attributes.shown = false; @@ -40,8 +40,7 @@ static bool bananos_initialize(uint32_t* display_w, uint32_t* display_h) return false; } - *display_w = dummy_or_error.value()->width(); - *display_h = dummy_or_error.value()->height(); + register_display(0, 0, dummy_or_error.value()->width(), dummy_or_error.value()->height()); if (auto ret = bananos_initialize_keymap(); ret.is_error()) { diff --git a/xbanan/main.cpp b/xbanan/main.cpp index afa380b..099be6c 100644 --- a/xbanan/main.cpp +++ b/xbanan/main.cpp @@ -19,6 +19,8 @@ #include #endif +CARD32 g_next_global_id { 1 }; + const xPixmapFormat g_formats[6] { { .depth = 1, @@ -58,7 +60,7 @@ const xDepth g_depth { }; const xVisualType g_visual { - .visualID = 1, + .visualID = g_next_global_id++, .c_class = TrueColor, .bitsPerRGB = 8, .colormapEntries = 256, @@ -68,24 +70,26 @@ const xVisualType g_visual { }; xWindowRoot g_root { - .windowId = 2, + .windowId = g_next_global_id++, .defaultColormap = 0, .whitePixel = 0xFFFFFF, .blackPixel = 0x000000, .currentInputMask = 0, .pixWidth = 0, .pixHeight = 0, - .mmWidth = 0, + .mmWidth = 0, .mmHeight = 0, .minInstalledMaps = 1, .maxInstalledMaps = 1, .rootVisualID = g_visual.visualID, .backingStore = 0, .saveUnders = 0, - .rootDepth = 24, + .rootDepth = g_depth.depth, .nDepths = 1, }; +BAN::Vector g_displays; + BAN::HashMap> g_objects; BAN::HashMap g_atoms_name_to_id; @@ -261,13 +265,53 @@ int main() APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE_UTILITY); #undef APPEND_ATOM_CUSTOM - uint32_t display_w, display_h; - if (!g_platform_ops.initialize(&display_w, &display_h)) + if (!g_platform_ops.initialize()) return 1; - g_root.pixWidth = display_w; - g_root.pixHeight = display_h; - g_root.mmWidth = static_cast(display_w * 254 / 960); // 96 DPI - g_root.mmHeight = static_cast(display_h * 254 / 960); // 96 DPI + + 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(display_min_x, display.x); + display_min_y = BAN::Math::min(display_min_y, display.y); + display_max_x = BAN::Math::max(display_max_x, display.x + display.w); + display_max_y = BAN::Math::max(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::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::create(Object { + .type = Object::Type::Visual, + })) + )); printf("xbanan started\n"); @@ -353,28 +397,6 @@ int main() } }; - Client dummy_client {}; - MUST(g_objects.insert(g_root.windowId, - MUST(BAN::UniqPtr::create(Object { - .type = Object::Type::Window, - .object = Object::Window { - .owner = dummy_client, - .mapped = true, - .depth = g_root.rootDepth, - .parent = None, - .c_class = InputOutput, - .width = g_root.pixWidth, - .height = g_root.pixHeight, - } - })) - )); - - MUST(g_objects.insert(g_visual.visualID, - MUST(BAN::UniqPtr::create(Object { - .type = Object::Type::Visual, - })) - )); - for (;;) { epoll_event events[16];