int TestApp::main(const std::vector<std::string> &args) { // Create a console window for text-output if not available ConsoleWindow console("Console"); try { DisplayWindowDescription desc; desc.set_size(Size(800,600), true); desc.set_title("Fullscreen test"); DisplayWindow window(desc); while (!window.get_ic().get_keyboard().get_keycode(keycode_escape)) { if (window.get_ic().get_keyboard().get_keycode(keycode_f11)) { desc.set_fullscreen(!desc.is_fullscreen()); window = DisplayWindow(desc); } window.get_gc().clear(Colorf::gray30); window.flip(); KeepAlive::process(); System::sleep(50); } } catch(Exception error) { Console::write_line("Exception caught:"); Console::write_line(error.message); console.display_close_message(); return -1; } return 0; }
void D3DDisplayWindowProvider::create(DisplayWindowSite *new_site, const DisplayWindowDescription &description) { site = new_site; if (device) D3DShareList::device_destroyed(device); info_queue.clear(); debug.clear(); back_buffer_rtv.clear(); fake_front_buffer.clear(); back_buffer.clear(); swap_chain.clear(); device_context.clear(); device.clear(); window.create(site, description); use_fake_front_buffer = description.is_update_supported(); D3D_FEATURE_LEVEL request_levels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 }; DXGI_SWAP_CHAIN_DESC swap_chain_description; swap_chain_description.BufferCount = description.get_flipping_buffers(); swap_chain_description.BufferDesc.Width = 0; swap_chain_description.BufferDesc.Height = 0; swap_chain_description.BufferDesc.RefreshRate.Numerator = 60; swap_chain_description.BufferDesc.RefreshRate.Denominator = 1; swap_chain_description.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; swap_chain_description.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; swap_chain_description.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; swap_chain_description.SampleDesc.Count = 1; swap_chain_description.SampleDesc.Quality = 0; swap_chain_description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_chain_description.OutputWindow = window.get_hwnd(); swap_chain_description.Windowed = TRUE; // Seems the documentation wants us to call IDXGISwapChain::SetFullscreenState afterwards swap_chain_description.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; swap_chain_description.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; bool debug_mode = false; // To do: fetch this from DisplayWindowDescription using same method as clanGL (or maybe promote debug flag to clanDisplay?) UINT device_flags = 0; if (debug_mode) device_flags |= D3D11_CREATE_DEVICE_DEBUG; MutexSection mutex_lock(&d3d11_mutex); if (d3d11_dll == 0) { d3d11_dll = LoadLibrary(L"d3d11.dll"); if (d3d11_dll == 0) throw Exception("Unable to load d3d11.dll"); try { d3d11_createdeviceandswapchain = reinterpret_cast<FuncD3D11CreateDeviceAndSwapChain>(GetProcAddress(d3d11_dll, "D3D11CreateDeviceAndSwapChain")); if (d3d11_createdeviceandswapchain == 0) throw Exception("D3D11CreateDeviceAndSwapChain function not found!"); } catch (...) { CloseHandle(d3d11_dll); d3d11_dll = 0; d3d11_createdeviceandswapchain = 0; throw; } } HRESULT result = d3d11_createdeviceandswapchain( 0, D3D_DRIVER_TYPE_HARDWARE, 0, device_flags, request_levels, 3, D3D11_SDK_VERSION, &swap_chain_description, swap_chain.output_variable(), device.output_variable(), &feature_level, device_context.output_variable()); D3DTarget::throw_if_failed("D3D11CreateDeviceAndSwapChain failed", result); if (debug_mode) { result = device->QueryInterface(__uuidof(ID3D11Debug), (void**)debug.output_variable()); if (FAILED(result)) debug.clear(); // No debug info available. Should this throw an exception instead? result = device->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)info_queue.output_variable()); if (FAILED(result)) info_queue.clear(); // No debug messages available. } // Disable mouse lag (no, 3 frames rendered ahead is NOT a good default Microsoft): ComPtr<IDXGIDevice1> dxgi_device; result = swap_chain->GetDevice(__uuidof(IDXGIDevice1), (void**)dxgi_device.output_variable()); D3DTarget::throw_if_failed("Unable to retrieve IDXGIDevice1 from swap chain", result); dxgi_device->SetMaximumFrameLatency(1); create_swap_chain_buffers(); gc = GraphicContext(new D3DGraphicContextProvider(this, description)); if (description.is_fullscreen()) swap_chain->SetFullscreenState(TRUE, 0); D3DGraphicContextProvider *d3d_gc = static_cast<D3DGraphicContextProvider*>(gc.get_provider()); d3d_gc->standard_programs = StandardPrograms(gc); }
void OpenGLWindowProvider::create(DisplayWindowSite *new_site, const DisplayWindowDescription &desc) { site = new_site; fullscreen = desc.is_fullscreen(); win32_window.create(site, desc); if (!opengl_context) { HWND handle = win32_window.get_hwnd(); dwm_layered = false; if (desc.is_layered() && !DwmFunctions::is_composition_enabled()) { create_shadow_window(handle); } else { if (desc.is_layered()) dwm_layered = true; } desc.is_layered() ? double_buffered = false : double_buffered = true; // Only can use Layered windows that are single buffered with OpenGL (via shadow window) ( PFD_DOUBLEBUFFER_DONTCARE set in OpenGLCreationHelper::set_multisampling_pixel_format) device_context = GetDC(handle); HGLRC share_context = get_share_context(); OpenGLCreationHelper helper(handle, device_context); helper.set_multisampling_pixel_format(desc); int gl_major = opengl_desc.get_version_major(); int gl_minor = opengl_desc.get_version_minor(); if (opengl_desc.get_allow_lower_versions() == false) { opengl_context = helper.create_opengl3_context(share_context, gl_major, gl_minor, opengl_desc); if (!opengl_context) throw Exception(string_format("This application requires OpenGL %1.%2 or above. Try updating your drivers, or upgrade to a newer graphics card.", gl_major, gl_minor)); } else { static const char opengl_version_list[] = { // Clanlib supported version pairs 4, 5, 4, 4, 4, 3, 4, 2, 4, 1, 4, 0, 3, 3, 3, 2, 3, 1, 3, 0, 0, 0, // End of list }; const char *opengl_version_list_ptr = opengl_version_list; do { int major = *(opengl_version_list_ptr++); if (major == 0) break; int minor = *(opengl_version_list_ptr++); // Find the appropriate version in the list if (major > gl_major) continue; if (major == gl_major) { if (minor > gl_minor) continue; } opengl_context = helper.create_opengl3_context(share_context, major, minor, opengl_desc); } while (!opengl_context); if (!opengl_context) opengl_context = helper.create_opengl2_context(share_context); if (!opengl_context) throw Exception("This application requires OpenGL. Try updating your drivers, or upgrade to a newer graphics card."); } bool use_gl3; int desc_version_major = opengl_desc.get_version_major(); int desc_version_minor = opengl_desc.get_version_minor(); // Do not attempt GL3, if not requested that version if (desc_version_major < 3) { use_gl3 = false; } else if (!opengl_desc.get_allow_lower_versions()) // Else, if we do not allow lower versions, only attempt GL3 { use_gl3 = true; } else { // Choose the target depending on the current opengl version int gl_version_major; int gl_version_minor; get_opengl_version(gl_version_major, gl_version_minor); if (gl_version_major < 3) { use_gl3 = false; } else { use_gl3 = true; } } if (use_gl3) { using_gl3 = true; gc = GraphicContext(new GL3GraphicContextProvider(this)); } else { using_gl3 = false; gc = GraphicContext(new GL1GraphicContextProvider(this)); } } wglSwapIntervalEXT = (ptr_wglSwapIntervalEXT)OpenGL::get_proc_address("wglSwapIntervalEXT"); swap_interval = desc.get_swap_interval(); if (wglSwapIntervalEXT && swap_interval != -1) wglSwapIntervalEXT(swap_interval); }
void X11Window::create(XVisualInfo *visual, DisplayWindowSite *new_site, const DisplayWindowDescription &desc) { site = new_site; // Reset all variables close_window(); handle.screen = visual->screen; atoms = X11Atoms(handle.display); int disp_width_px = XDisplayWidth(handle.display, handle.screen); int disp_height_px = XDisplayHeight(handle.display, handle.screen); int disp_width_mm = XDisplayWidthMM(handle.display, handle.screen); // Get DPI of screen or use 96.0f if Xlib doesn't have a value. ppi = (disp_width_mm < 24) ? 96.0f : (25.4f * static_cast<float>(disp_width_px) / static_cast<float>(disp_width_mm)); // Update pixel ratio. set_pixel_ratio(pixel_ratio); // Get X11 root window. auto _root_window = RootWindow(handle.display, handle.screen); // Get and validate initial window position and size. int win_x = desc.get_position().left * pixel_ratio; int win_y = desc.get_position().top * pixel_ratio; int win_width = desc.get_size().width * pixel_ratio; int win_height = desc.get_size().height * pixel_ratio; if (win_width <= 0) throw Exception("Invalid window width."); if (win_height <= 0) throw Exception("Invalid window height."); // Set values if fullscreen requested. if (desc.is_fullscreen()) { win_x = 0; win_y = 0; win_width = disp_width_px; win_height = disp_height_px; } // Center window if position supplied is (-1, -1) if (win_x == -1 && win_y == -1) { win_x = (disp_width_px - win_width)/2 - 1; win_y = (disp_height_px - win_height)/2 - 1; } // Set minimum and maximum size this->resize_allowed = desc.get_allow_resize() || desc.is_fullscreen(); // Fullscreen mode needs a resizable window. if (resize_allowed) { minimum_size = Size(_ResizeMinimumSize_, _ResizeMinimumSize_); maximum_size = Size(0, 0); // No maximum size by default. } else { minimum_size = Size(win_width, win_height); maximum_size = Size(win_width, win_height); } // Setup X11 size hints. this->size_hints = XAllocSizeHints(); if (size_hints == NULL) throw Exception("Failed to allocate X11 XSizeHints structure."); size_hints->flags = PMinSize | (resize_allowed ? 0 : PMaxSize); size_hints->flags |= PResizeInc | PBaseSize | PWinGravity; // x, y, width, height are obsolete. size_hints->flags |= USSize | USPosition; // See http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472522864 size_hints->min_width = minimum_size.width; size_hints->min_height = minimum_size.height; size_hints->max_width = maximum_size.width; size_hints->max_height = maximum_size.height; size_hints->width_inc = 1; size_hints->height_inc = 1; size_hints->base_width = win_width; size_hints->base_height = win_height; size_hints->win_gravity = NorthWestGravity; // Setup X11 colormap. // // The X.Org XServer implementation used on most systems requires that // a color-map be set for the window. Additionally, windows with a // different color-depth than its parent must have the border-pixel flag // set when creating them. Failure to do either will cause XCreateWindow() // to result in a BadMatch error. // // Source: stackoverflow.com/questions/3645632 color_map = XCreateColormap(handle.display, _root_window, visual->visual, AllocNone); // Static popups are unresizable captionless popup windows. // These windows should not be decorated. bool is_static_popup = desc.is_popup() && !desc.has_caption() && !desc.get_allow_resize(); // Tell X11 to perserve graphical content under small popup windows to avoid redraws. bool save_under = desc.is_popup() && ( (win_width * win_height) < (256 * 256 * pixel_ratio * pixel_ratio) ); // Setup window attributes. XSetWindowAttributes attr = XSetWindowAttributes { .background_pixmap = None, /* default */ .background_pixel = 0ul, /* default: undefined */ .border_pixmap = CopyFromParent, /* default */ .border_pixel = 0ul, /* see color_map details above */ .bit_gravity = ForgetGravity, /* default */ .win_gravity = NorthWestGravity, /* default */ .backing_store = NotUseful, /* default */ .backing_planes = -1ul, /* default */ .backing_pixel = 0ul, /* default */ .save_under = save_under ? True : False, .event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | KeymapStateMask | ExposureMask // | VisibilityChangeMask | StructureNotifyMask | FocusChangeMask | PropertyChangeMask , .do_not_propagate_mask = NoEventMask, /* default */ .override_redirect = is_static_popup ? True : False, .colormap = color_map, /* see color_map details above */ .cursor = None /* default; Let X11 handle the cursor for now. */ }; this->system_cursor = XCreateFontCursor(handle.display, XC_left_ptr); // This is allowed to fail log_event("debug", "clan::X11Window::create(): Creating window..."); log_event("debug", " x%1 y%2 w%3 h%4 b%5 d%6", win_x, win_y, win_width, win_height, border_width, visual->depth); log_event("debug", " a.su%1, a.od%2", save_under, is_static_popup); // Create window handle.window = XCreateWindow( handle.display, _root_window, win_x, win_y, win_width, win_height, border_width, visual->depth, InputOutput, visual->visual, CWBorderPixel | CWOverrideRedirect | CWSaveUnder | CWEventMask | CWColormap, &attr ); if (!handle.window) throw Exception("Unable to create the X11 window"); if (!desc.get_owner().is_null()) { DisplayWindow owner = desc.get_owner(); XSetTransientForHint(handle.display, handle.window, owner.get_handle().window); } // Setup the hidden cursor (Maybe this should be done only once when required) char data[64]; // 8x8 memset(data, 0, 64); XColor black_color; memset(&black_color, 0, sizeof(black_color)); cursor_bitmap = XCreateBitmapFromData(handle.display, handle.window, data, 8, 8); hidden_cursor = XCreatePixmapCursor(handle.display, cursor_bitmap, cursor_bitmap, &black_color, &black_color, 0,0); // Set title of window: set_title(desc.get_title()); { // Inform the window manager who we are, so it can kill us if we're not good for its universe. Atom atom; int32_t pid = getpid(); if (pid > 0) { atom = atoms.get_atom(handle.display, "_NET_WM_PID", False); XChangeProperty(handle.display, handle.window, atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &pid, 1); } char hostname[256]; if (gethostname(hostname, sizeof(hostname)) > -1) { hostname[255] = 0; atom = atoms.get_atom(handle.display, "WM_CLIENT_MACHINE", False); XChangeProperty(handle.display, handle.window, atom, XA_STRING, 8, PropModeReplace, (unsigned char *) hostname, strlen(hostname)); } } // Set-up window type/styling. // TODO Support more window types, broaden ClanLib window type support, etc. if (atoms["_NET_WM_WINDOW_TYPE"] != None) { Atom type = None; std::string name; if (desc.is_dialog()) { name = "_NET_WM_WINDOW_TYPE_DIALOG"; type = atoms[name]; } else if (desc.is_popup()) { if (is_static_popup) { name = "_NET_WM_WINDOW_TYPE_TOOLTIP"; type = atoms[name]; } else if (desc.has_caption()) // A pop-up with title bar -> utility { name = "_NET_WM_WINDOW_TYPE_UTILITY"; type = atoms[name]; } // else, a pop-up without a title bar -> popup-menu, combo, dropdown, tooltip, ... if (type == None) { name = "_NET_WM_WINDOW_TYPE_POPUP_MENU"; type = atoms[name]; } if (type == None) { name = "_NET_WM_WINDOW_TYPE_COMBO"; type = atoms[name]; } if (type == None) { name = "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"; type = atoms[name]; } } // else if (desc.is_normal()) // Fallback to normal window type if WM doesn't support what we want. if (type == None) { name = "_NET_WM_WINDOW_TYPE_NORMAL"; type = atoms[name]; } if (type != None) // Ensure selected type exists. { XChangeProperty(handle.display, handle.window, atoms["_NET_WM_WINDOW_TYPE"], XA_ATOM, 32, PropModeReplace, (unsigned char *)&type, 1); log_event("debug", "clan::X11Window::create(): Creating window of type '%1'.", name); } else { log_event("debug", "clan::X11Window::create(): Failed to find a suitable window type."); } } else { log_event("debug", "clan::X11Window::create(): _NET_WM_WINDOW_TYPE does not exist."); } // Set size hints XSetWMNormalHints(handle.display, handle.window, size_hints); { // Subscribe to WM events. Atom protocol = atoms["WM_DELETE_WINDOW"]; Status result = XSetWMProtocols(handle.display, handle.window, &protocol, 1); if (result == 0) log_event("debug", "clan::X11Window::create(): Failed to set WM_PROTOCOLS."); } { // Make auto-repeat keys detectable. Bool supports_detectable_autorepeat; XkbSetDetectableAutoRepeat(handle.display, True, &supports_detectable_autorepeat); } { // Make window full-screen if requested. if (atoms["_NET_WM_STATE"] == None && atoms["_NET_WM_STATE_FULLSCREEN"]) { fullscreen = false; log_event("debug", "clan::X11Window: Fullscreen not supported by WM."); } else { fullscreen = desc.is_fullscreen(); } if (fullscreen) { Atom state = atoms["_NET_WM_STATE_FULLSCREEN"]; XChangeProperty(handle.display, handle.window, atoms["_NET_WM_STATE"], XA_ATOM, 32, PropModeReplace, (unsigned char *)&state, 1); } } update_frame_extents(); auto new_client_area = desc.get_position_client_area() // supplied position is at ? client area : window area; ? Rect::xywh(win_x, win_y, win_width, win_height) : Rect::xywh(win_x + frame_extents.left, win_y + frame_extents.right, win_width, win_height) ; process_window_resize(new_client_area); // Set window visibility if (desc.is_visible()) { show(false); } // Setup the clipboard clipboard.setup(); // Go looking for joysticks: setup_joysticks(); } void X11Window::update_frame_extents() { frame_extents = Rect { border_width, border_width, border_width, border_width }; if (atoms["_NET_FRAME_EXTENTS"] == None) return; // Request frame extents from WM. if (atoms["_NET_REQUEST_FRAME_EXTENTS"] != None) { XEvent event; memset(&event, 0, sizeof(event)); event.type = ClientMessage; event.xclient.window = handle.window; event.xclient.format = 32; event.xclient.message_type = atoms["_NET_REQUEST_FRAME_EXTENTS"]; XSendEvent(handle.display, RootWindow(handle.display, handle.screen), False, SubstructureNotifyMask | SubstructureRedirectMask, &event); int timer = 10; while(true) { if (timer < 0) { log_event("debug", "clan::X11Window: Your window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation."); break; } if (XCheckMaskEvent(handle.display, PropertyNotify, &event)) { break; } clan::System::sleep(5); timer--; } } unsigned long item_count; // _NET_FRAME_EXTENTS, left, right, top, bottom, CARDINAL[4]/32 unsigned char *data = atoms.get_property(handle.window, "_NET_FRAME_EXTENTS", item_count); if (data == NULL) return; if (item_count >= 4) { long *cardinal = (long *)data; frame_extents.left = cardinal[0]; frame_extents.right = cardinal[1]; frame_extents.top = cardinal[2]; frame_extents.bottom = cardinal[3]; } XFree(data); }