Merge pull request #26312 from garbear/update-controllers
[xbmc.git] / xbmc / windowing / X11 / WinSystemX11.cpp
blob562df661feebbefdeff0da638231a3d801ced76c
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "WinSystemX11.h"
11 #include "CompileInfo.h"
12 #include "OSScreenSaverX11.h"
13 #include "ServiceBroker.h"
14 #include "WinEventsX11.h"
15 #include "XRandR.h"
16 #include "guilib/DispResource.h"
17 #include "guilib/Texture.h"
18 #include "input/InputManager.h"
19 #include "messaging/ApplicationMessenger.h"
20 #include "settings/DisplaySettings.h"
21 #include "settings/Settings.h"
22 #include "settings/SettingsComponent.h"
23 #include "settings/lib/Setting.h"
24 #include "utils/StringUtils.h"
25 #include "utils/TimeUtils.h"
26 #include "utils/log.h"
27 #include "windowing/GraphicContext.h"
29 #include <memory>
30 #include <mutex>
31 #include <string>
32 #include <vector>
34 #include <X11/Xatom.h>
35 #include <X11/extensions/Xrandr.h>
37 using namespace KODI::WINDOWING::X11;
39 using namespace std::chrono_literals;
41 #define EGL_NO_CONFIG (EGLConfig)0
43 CWinSystemX11::CWinSystemX11() : CWinSystemBase()
45 m_dpy = NULL;
46 m_bWasFullScreenBeforeMinimize = false;
47 m_minimized = false;
48 m_bIgnoreNextFocusMessage = false;
49 m_bIsInternalXrr = false;
50 m_delayDispReset = false;
52 XSetErrorHandler(XErrorHandler);
54 m_winEventsX11 = new CWinEventsX11(*this);
55 m_winEvents.reset(m_winEventsX11);
58 CWinSystemX11::~CWinSystemX11() = default;
60 bool CWinSystemX11::InitWindowSystem()
62 const char* env = getenv("DISPLAY");
63 if (!env)
65 CLog::Log(LOGDEBUG, "CWinSystemX11::{} - DISPLAY env not set", __FUNCTION__);
66 return false;
69 if ((m_dpy = XOpenDisplay(NULL)))
71 bool ret = CWinSystemBase::InitWindowSystem();
73 CServiceBroker::GetSettingsComponent()
74 ->GetSettings()
75 ->GetSetting(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE)
76 ->SetVisible(true);
78 return ret;
80 else
81 CLog::Log(LOGERROR, "X11 Error: No Display found");
83 return false;
86 bool CWinSystemX11::DestroyWindowSystem()
88 //restore desktop resolution on exit
89 if (m_bFullScreen)
91 XOutput out;
92 XMode mode;
93 out.name = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).strOutput;
94 mode.w = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iWidth;
95 mode.h = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iHeight;
96 mode.hz = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).fRefreshRate;
97 mode.id = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).strId;
98 g_xrandr.SetMode(out, mode);
101 return true;
104 bool CWinSystemX11::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
106 if(!SetFullScreen(fullScreen, res, false))
107 return false;
109 m_bWindowCreated = true;
110 return true;
113 bool CWinSystemX11::DestroyWindow()
115 if (!m_mainWindow)
116 return true;
118 if (m_invisibleCursor)
120 XUndefineCursor(m_dpy, m_mainWindow);
121 XFreeCursor(m_dpy, m_invisibleCursor);
122 m_invisibleCursor = 0;
125 m_winEventsX11->Quit();
127 XUnmapWindow(m_dpy, m_mainWindow);
128 XDestroyWindow(m_dpy, m_glWindow);
129 XDestroyWindow(m_dpy, m_mainWindow);
130 m_glWindow = 0;
131 m_mainWindow = 0;
133 if (m_icon)
134 XFreePixmap(m_dpy, m_icon);
136 return true;
139 bool CWinSystemX11::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
141 m_userOutput = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
142 XOutput *out = NULL;
143 if (m_userOutput.compare("Default") != 0)
145 out = g_xrandr.GetOutput(m_userOutput);
146 if (out)
148 XMode mode = g_xrandr.GetCurrentMode(m_userOutput);
149 if (!mode.isCurrent)
151 out = NULL;
155 if (!out)
157 std::vector<XOutput> outputs = g_xrandr.GetModes();
158 if (!outputs.empty())
160 m_userOutput = outputs[0].name;
164 if (!SetWindow(newWidth, newHeight, false, m_userOutput))
166 return false;
169 m_nWidth = newWidth;
170 m_nHeight = newHeight;
171 m_bFullScreen = false;
172 m_currentOutput = m_userOutput;
174 return true;
177 void CWinSystemX11::FinishWindowResize(int newWidth, int newHeight)
179 m_userOutput = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
180 XOutput *out = NULL;
181 if (m_userOutput.compare("Default") != 0)
183 out = g_xrandr.GetOutput(m_userOutput);
184 if (out)
186 XMode mode = g_xrandr.GetCurrentMode(m_userOutput);
187 if (!mode.isCurrent)
189 out = NULL;
193 if (!out)
195 std::vector<XOutput> outputs = g_xrandr.GetModes();
196 if (!outputs.empty())
198 m_userOutput = outputs[0].name;
202 XResizeWindow(m_dpy, m_glWindow, newWidth, newHeight);
203 UpdateCrtc();
205 if (m_userOutput.compare(m_currentOutput) != 0)
207 SetWindow(newWidth, newHeight, false, m_userOutput);
210 m_nWidth = newWidth;
211 m_nHeight = newHeight;
214 bool CWinSystemX11::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
216 XOutput out;
217 XMode mode;
219 if (fullScreen)
221 out.name = res.strOutput;
222 mode.w = res.iWidth;
223 mode.h = res.iHeight;
224 mode.hz = res.fRefreshRate;
225 mode.id = res.strId;
227 else
229 out.name = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).strOutput;
230 mode.w = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iWidth;
231 mode.h = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iHeight;
232 mode.hz = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).fRefreshRate;
233 mode.id = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).strId;
236 XMode currmode = g_xrandr.GetCurrentMode(out.name);
237 if (!currmode.name.empty())
239 // flip h/w when rotated
240 if (m_bIsRotated)
242 int w = mode.w;
243 mode.w = mode.h;
244 mode.h = w;
247 // only call xrandr if mode changes
248 if (m_mainWindow)
250 if (currmode.w != mode.w || currmode.h != mode.h ||
251 currmode.hz != mode.hz || currmode.id != mode.id)
253 CLog::Log(LOGINFO, "CWinSystemX11::SetFullScreen - calling xrandr");
255 // remember last position of mouse
256 Window root_return, child_return;
257 int root_x_return, root_y_return;
258 int win_x_return, win_y_return;
259 unsigned int mask_return;
260 bool isInWin = XQueryPointer(m_dpy, m_mainWindow, &root_return, &child_return,
261 &root_x_return, &root_y_return,
262 &win_x_return, &win_y_return,
263 &mask_return);
265 if (isInWin)
267 m_MouseX = win_x_return;
268 m_MouseY = win_y_return;
270 else
272 m_MouseX = -1;
273 m_MouseY = -1;
276 OnLostDevice();
277 m_bIsInternalXrr = true;
278 g_xrandr.SetMode(out, mode);
279 auto delay =
280 std::chrono::milliseconds(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
281 "videoscreen.delayrefreshchange") *
282 100);
283 if (delay > 0ms)
285 m_delayDispReset = true;
286 m_dispResetTimer.Set(delay);
288 return true;
293 if (!SetWindow(res.iWidth, res.iHeight, fullScreen, m_userOutput))
294 return false;
296 m_nWidth = res.iWidth;
297 m_nHeight = res.iHeight;
298 m_bFullScreen = fullScreen;
299 m_currentOutput = m_userOutput;
301 return true;
304 void CWinSystemX11::UpdateResolutions()
306 CWinSystemBase::UpdateResolutions();
308 int numScreens = XScreenCount(m_dpy);
309 g_xrandr.SetNumScreens(numScreens);
311 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
312 bool switchOnOff = settings->GetBool(CSettings::SETTING_VIDEOSCREEN_BLANKDISPLAYS);
313 m_userOutput = settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
314 if (m_userOutput.compare("Default") == 0)
315 switchOnOff = false;
317 if(g_xrandr.Query(true, !switchOnOff))
319 XOutput *out = NULL;
320 if (m_userOutput.compare("Default") != 0)
322 out = g_xrandr.GetOutput(m_userOutput);
323 if (out)
325 XMode mode = g_xrandr.GetCurrentMode(m_userOutput);
326 if (!mode.isCurrent && !switchOnOff)
328 out = NULL;
332 if (!out)
334 m_userOutput = g_xrandr.GetModes()[0].name;
335 out = g_xrandr.GetOutput(m_userOutput);
338 if (switchOnOff)
340 // switch on output
341 g_xrandr.TurnOnOutput(m_userOutput);
343 // switch off other outputs
344 std::vector<XOutput> outputs = g_xrandr.GetModes();
345 for (size_t i=0; i<outputs.size(); i++)
347 if (StringUtils::EqualsNoCase(outputs[i].name, m_userOutput))
348 continue;
349 g_xrandr.TurnOffOutput(outputs[i].name);
353 XMode mode = g_xrandr.GetCurrentMode(m_userOutput);
354 if (mode.id.empty())
355 mode = g_xrandr.GetPreferredMode(m_userOutput);
356 m_bIsRotated = out->isRotated;
357 if (!m_bIsRotated)
358 UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), out->name, mode.w, mode.h, mode.hz, 0);
359 else
360 UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), out->name, mode.h, mode.w, mode.hz, 0);
361 CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).strId = mode.id;
363 else
365 m_userOutput = "No Output";
366 m_screen = DefaultScreen(m_dpy);
367 int w = DisplayWidth(m_dpy, m_screen);
368 int h = DisplayHeight(m_dpy, m_screen);
369 UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), m_userOutput, w, h, 0.0, 0);
372 // erase previous stored modes
373 CDisplaySettings::GetInstance().ClearCustomResolutions();
375 CLog::Log(LOGINFO, "Available videomodes (xrandr):");
377 XOutput *out = g_xrandr.GetOutput(m_userOutput);
378 if (out != NULL)
380 CLog::Log(LOGINFO, "Output '{}' has {} modes", out->name, out->modes.size());
382 for (auto mode : out->modes)
384 CLog::Log(LOGINFO, "ID:{} Name:{} Refresh:{:f} Width:{} Height:{}", mode.id, mode.name,
385 mode.hz, mode.w, mode.h);
386 RESOLUTION_INFO res;
387 res.dwFlags = 0;
389 if (mode.IsInterlaced())
390 res.dwFlags |= D3DPRESENTFLAG_INTERLACED;
392 if (!m_bIsRotated)
394 res.iWidth = mode.w;
395 res.iHeight = mode.h;
396 res.iScreenWidth = mode.w;
397 res.iScreenHeight = mode.h;
399 else
401 res.iWidth = mode.h;
402 res.iHeight = mode.w;
403 res.iScreenWidth = mode.h;
404 res.iScreenHeight = mode.w;
407 if (mode.h > 0 && mode.w > 0 && out->hmm > 0 && out->wmm > 0)
408 res.fPixelRatio = ((float)out->wmm/(float)mode.w) / (((float)out->hmm/(float)mode.h));
409 else
410 res.fPixelRatio = 1.0f;
412 CLog::Log(LOGINFO, "Pixel Ratio: {:f}", res.fPixelRatio);
414 res.strMode = StringUtils::Format("{}: {} @ {:.2f}Hz", out->name, mode.name, mode.hz);
415 res.strOutput = out->name;
416 res.strId = mode.id;
417 res.iSubtitles = mode.h;
418 res.fRefreshRate = mode.hz;
419 res.bFullScreen = true;
421 CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res);
422 CDisplaySettings::GetInstance().AddResolutionInfo(res);
425 CDisplaySettings::GetInstance().ApplyCalibrations();
428 bool CWinSystemX11::HasCalibration(const RESOLUTION_INFO &resInfo)
430 XOutput *out = g_xrandr.GetOutput(m_currentOutput);
432 // keep calibrations done on a not connected output
433 if (!StringUtils::EqualsNoCase(out->name, resInfo.strOutput))
434 return true;
436 // keep calibrations not updated with resolution data
437 if (resInfo.iWidth == 0)
438 return true;
440 float fPixRatio;
441 if (resInfo.iHeight>0 && resInfo.iWidth>0 && out->hmm>0 && out->wmm>0)
442 fPixRatio = ((float)out->wmm/(float)resInfo.iWidth) / (((float)out->hmm/(float)resInfo.iHeight));
443 else
444 fPixRatio = 1.0f;
446 if (resInfo.Overscan.left != 0)
447 return true;
448 if (resInfo.Overscan.top != 0)
449 return true;
450 if (resInfo.Overscan.right != resInfo.iWidth)
451 return true;
452 if (resInfo.Overscan.bottom != resInfo.iHeight)
453 return true;
454 if (resInfo.fPixelRatio != fPixRatio)
455 return true;
456 if (resInfo.iSubtitles != resInfo.iHeight)
457 return true;
459 return false;
462 bool CWinSystemX11::UseLimitedColor()
464 return CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE);
467 std::vector<std::string> CWinSystemX11::GetConnectedOutputs()
469 std::vector<std::string> outputs;
470 std::vector<XOutput> outs;
471 g_xrandr.Query(true);
472 outs = g_xrandr.GetModes();
473 outputs.emplace_back("Default");
474 for(unsigned int i=0; i<outs.size(); ++i)
476 outputs.emplace_back(outs[i].name);
479 return outputs;
482 bool CWinSystemX11::IsCurrentOutput(const std::string& output)
484 return (StringUtils::EqualsNoCase(output, "Default")) || (m_currentOutput.compare(output.c_str()) == 0);
487 void CWinSystemX11::ShowOSMouse(bool show)
489 if (show)
490 XUndefineCursor(m_dpy,m_mainWindow);
491 else if (m_invisibleCursor)
492 XDefineCursor(m_dpy,m_mainWindow, m_invisibleCursor);
495 std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> CWinSystemX11::GetOSScreenSaverImpl()
497 std::unique_ptr<IOSScreenSaver> ret;
498 if (m_dpy)
500 ret = std::make_unique<COSScreenSaverX11>(m_dpy);
502 return ret;
505 void CWinSystemX11::NotifyAppActiveChange(bool bActivated)
507 if (bActivated && m_bWasFullScreenBeforeMinimize && !m_bFullScreen)
509 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
511 m_bWasFullScreenBeforeMinimize = false;
513 m_minimized = !bActivated;
516 void CWinSystemX11::NotifyAppFocusChange(bool bGaining)
518 if (bGaining && m_bWasFullScreenBeforeMinimize && !m_bIgnoreNextFocusMessage &&
519 !m_bFullScreen)
521 m_bWasFullScreenBeforeMinimize = false;
522 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
523 m_minimized = false;
525 if (!bGaining)
526 m_bIgnoreNextFocusMessage = false;
529 bool CWinSystemX11::Minimize()
531 m_bWasFullScreenBeforeMinimize = m_bFullScreen;
532 if (m_bWasFullScreenBeforeMinimize)
534 m_bIgnoreNextFocusMessage = true;
535 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
538 XIconifyWindow(m_dpy, m_mainWindow, m_screen);
540 m_minimized = true;
541 return true;
543 bool CWinSystemX11::Restore()
545 return false;
547 bool CWinSystemX11::Hide()
549 XUnmapWindow(m_dpy, m_mainWindow);
550 XFlush(m_dpy);
551 return true;
553 bool CWinSystemX11::Show(bool raise)
555 XMapWindow(m_dpy, m_mainWindow);
556 XFlush(m_dpy);
557 m_minimized = false;
558 return true;
561 void CWinSystemX11::NotifyXRREvent()
563 CLog::Log(LOGDEBUG, "{} - notify display reset event", __FUNCTION__);
565 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
567 if (!g_xrandr.Query(true))
569 CLog::Log(LOGERROR, "WinSystemX11::RefreshWindow - failed to query xrandr");
570 return;
573 // if external event update resolutions
574 if (!m_bIsInternalXrr)
576 UpdateResolutions();
579 RecreateWindow();
582 void CWinSystemX11::RecreateWindow()
584 m_windowDirty = true;
586 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
588 XOutput *out = g_xrandr.GetOutput(m_userOutput);
589 XMode mode = g_xrandr.GetCurrentMode(m_userOutput);
591 if (out)
592 CLog::Log(LOGDEBUG, "{} - current output: {}, mode: {}, refresh: {:.3f}", __FUNCTION__,
593 out->name, mode.id, mode.hz);
594 else
595 CLog::Log(LOGWARNING, "{} - output name not set", __FUNCTION__);
597 RESOLUTION_INFO res;
598 unsigned int i;
599 bool found(false);
600 for (i = RES_DESKTOP; i < CDisplaySettings::GetInstance().ResolutionInfoSize(); ++i)
602 res = CDisplaySettings::GetInstance().GetResolutionInfo(i);
603 if (StringUtils::EqualsNoCase(CDisplaySettings::GetInstance().GetResolutionInfo(i).strId, mode.id))
605 found = true;
606 break;
610 if (!found)
612 CLog::Log(LOGERROR, "CWinSystemX11::RecreateWindow - could not find resolution");
613 i = RES_DESKTOP;
616 if (CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot())
617 CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution((RESOLUTION)i, true);
618 else
619 CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(RES_WINDOW, true);
622 void CWinSystemX11::OnLostDevice()
624 CLog::Log(LOGDEBUG, "{} - notify display change event", __FUNCTION__);
627 std::unique_lock<CCriticalSection> lock(m_resourceSection);
628 for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); ++i)
629 (*i)->OnLostDisplay();
632 m_winEventsX11->SetXRRFailSafeTimer(3s);
635 void CWinSystemX11::Register(IDispResource *resource)
637 std::unique_lock<CCriticalSection> lock(m_resourceSection);
638 m_resources.push_back(resource);
641 void CWinSystemX11::Unregister(IDispResource* resource)
643 std::unique_lock<CCriticalSection> lock(m_resourceSection);
644 std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
645 if (i != m_resources.end())
646 m_resources.erase(i);
649 int CWinSystemX11::XErrorHandler(Display* dpy, XErrorEvent* error)
651 char buf[1024];
652 XGetErrorText(error->display, error->error_code, buf, sizeof(buf));
653 CLog::Log(LOGERROR,
654 "CWinSystemX11::XErrorHandler: {}, type:{}, serial:{}, error_code:{}, request_code:{} "
655 "minor_code:{}",
656 buf, error->type, error->serial, (int)error->error_code, (int)error->request_code,
657 (int)error->minor_code);
659 return 0;
662 bool CWinSystemX11::SetWindow(int width, int height, bool fullscreen, const std::string &output, int *winstate)
664 bool changeWindow = false;
665 bool changeSize = false;
666 float mouseX = 0.5;
667 float mouseY = 0.5;
669 if (!m_mainWindow)
671 CServiceBroker::GetInputManager().SetMouseActive(false);
674 if (m_mainWindow && ((m_bFullScreen != fullscreen) || m_currentOutput.compare(output) != 0 || m_windowDirty))
676 // set mouse to last known position
677 // we can't trust values after an xrr event
678 if (m_bIsInternalXrr && m_MouseX >= 0 && m_MouseY >= 0)
680 mouseX = (float)m_MouseX/m_nWidth;
681 mouseY = (float)m_MouseY/m_nHeight;
683 else if (!m_windowDirty)
685 Window root_return, child_return;
686 int root_x_return, root_y_return;
687 int win_x_return, win_y_return;
688 unsigned int mask_return;
689 bool isInWin = XQueryPointer(m_dpy, m_mainWindow, &root_return, &child_return,
690 &root_x_return, &root_y_return,
691 &win_x_return, &win_y_return,
692 &mask_return);
694 if (isInWin)
696 mouseX = (float)win_x_return/m_nWidth;
697 mouseY = (float)win_y_return/m_nHeight;
701 CServiceBroker::GetInputManager().SetMouseActive(false);
702 OnLostDevice();
703 DestroyWindow();
704 m_windowDirty = true;
707 // create main window
708 if (!m_mainWindow)
710 Colormap cmap;
711 XSetWindowAttributes swa;
712 XVisualInfo *vi;
713 int x0 = 0;
714 int y0 = 0;
716 XOutput *out = g_xrandr.GetOutput(output);
717 if (!out)
718 out = g_xrandr.GetOutput(m_currentOutput);
719 if (out)
721 m_screen = out->screen;
722 x0 = out->x;
723 y0 = out->y;
726 vi = GetVisual();
727 if (!vi)
729 CLog::Log(LOGERROR, "Failed to find matching visual");
730 return false;
733 cmap = XCreateColormap(m_dpy, RootWindow(m_dpy, vi->screen), vi->visual, AllocNone);
735 bool hasWM = HasWindowManager();
737 int def_vis = (vi->visual == DefaultVisual(m_dpy, vi->screen));
738 swa.override_redirect = hasWM ? False : True;
739 swa.border_pixel = fullscreen ? 0 : 5;
740 swa.background_pixel = def_vis ? BlackPixel(m_dpy, vi->screen) : 0;
741 swa.colormap = cmap;
742 swa.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask |
743 ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
744 PropertyChangeMask | StructureNotifyMask | KeymapStateMask |
745 EnterWindowMask | LeaveWindowMask | ExposureMask;
746 unsigned long mask = CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect | CWEventMask;
748 m_mainWindow = XCreateWindow(m_dpy, RootWindow(m_dpy, vi->screen),
749 x0, y0, width, height, 0, vi->depth,
750 InputOutput, vi->visual,
751 mask, &swa);
753 swa.override_redirect = False;
754 swa.border_pixel = 0;
755 swa.event_mask = ExposureMask;
756 mask = CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect | CWColormap | CWEventMask;
758 m_glWindow = XCreateWindow(m_dpy, m_mainWindow,
759 0, 0, width, height, 0, vi->depth,
760 InputOutput, vi->visual,
761 mask, &swa);
763 if (fullscreen && hasWM)
765 Atom fs = XInternAtom(m_dpy, "_NET_WM_STATE_FULLSCREEN", True);
766 XChangeProperty(m_dpy, m_mainWindow, XInternAtom(m_dpy, "_NET_WM_STATE", True), XA_ATOM, 32, PropModeReplace, (unsigned char *) &fs, 1);
767 // disable desktop compositing for KDE, when Kodi is in full-screen mode
768 long one = 1;
769 Atom composite = XInternAtom(m_dpy, "_KDE_NET_WM_BLOCK_COMPOSITING", True);
770 if (composite != None)
772 XChangeProperty(m_dpy, m_mainWindow, XInternAtom(m_dpy, "_KDE_NET_WM_BLOCK_COMPOSITING", True), XA_CARDINAL, 32,
773 PropModeReplace, (unsigned char*) &one, 1);
775 composite = XInternAtom(m_dpy, "_NET_WM_BYPASS_COMPOSITOR", True);
776 if (composite != None)
778 // standard way for Gnome 3
779 XChangeProperty(m_dpy, m_mainWindow, XInternAtom(m_dpy, "_NET_WM_BYPASS_COMPOSITOR", True), XA_CARDINAL, 32,
780 PropModeReplace, (unsigned char*) &one, 1);
784 // define invisible cursor
785 Pixmap bitmapNoData;
786 XColor black;
787 static char noData[] = { 0,0,0,0,0,0,0,0 };
788 black.red = black.green = black.blue = 0;
790 bitmapNoData = XCreateBitmapFromData(m_dpy, m_mainWindow, noData, 8, 8);
791 m_invisibleCursor = XCreatePixmapCursor(m_dpy, bitmapNoData, bitmapNoData,
792 &black, &black, 0, 0);
793 XFreePixmap(m_dpy, bitmapNoData);
794 XDefineCursor(m_dpy,m_mainWindow, m_invisibleCursor);
795 XFree(vi);
797 //init X11 events
798 m_winEventsX11->Init(m_dpy, m_mainWindow);
800 changeWindow = true;
801 changeSize = true;
804 if (!m_winEventsX11->HasStructureChanged() && ((width != m_nWidth) || (height != m_nHeight)))
806 changeSize = true;
809 if (changeSize || changeWindow)
811 XResizeWindow(m_dpy, m_mainWindow, width, height);
814 if ((width != m_nWidth) || (height != m_nHeight) || changeWindow)
816 XResizeWindow(m_dpy, m_glWindow, width, height);
819 if (changeWindow)
821 m_icon = None;
823 CreateIconPixmap();
824 XWMHints *wm_hints;
825 XClassHint *class_hints;
826 XTextProperty windowName, iconName;
828 std::string titleString = CCompileInfo::GetAppName();
829 const std::string& classString = titleString;
830 char *title = const_cast<char*>(titleString.c_str());
832 XStringListToTextProperty(&title, 1, &windowName);
833 XStringListToTextProperty(&title, 1, &iconName);
835 wm_hints = XAllocWMHints();
836 wm_hints->initial_state = NormalState;
837 wm_hints->icon_pixmap = m_icon;
838 wm_hints->flags = StateHint | IconPixmapHint;
840 class_hints = XAllocClassHint();
841 class_hints->res_class = const_cast<char*>(classString.c_str());
842 class_hints->res_name = const_cast<char*>(classString.c_str());
844 XSetWMProperties(m_dpy, m_mainWindow, &windowName, &iconName,
845 NULL, 0, NULL, wm_hints,
846 class_hints);
847 XFree(class_hints);
848 XFree(wm_hints);
849 XFree(iconName.value);
850 XFree(windowName.value);
852 // register interest in the delete window message
853 Atom wmDeleteMessage = XInternAtom(m_dpy, "WM_DELETE_WINDOW", False);
854 XSetWMProtocols(m_dpy, m_mainWindow, &wmDeleteMessage, 1);
857 // placement of window may follow mouse
858 XWarpPointer(m_dpy, None, m_mainWindow, 0, 0, 0, 0, mouseX*width, mouseY*height);
860 XMapRaised(m_dpy, m_glWindow);
861 XMapRaised(m_dpy, m_mainWindow);
863 // discard events generated by creating the window, i.e. xrr events
864 XSync(m_dpy, True);
866 if (winstate)
867 *winstate = 1;
870 UpdateCrtc();
872 return true;
875 bool CWinSystemX11::CreateIconPixmap()
877 int depth;
878 XImage *img = NULL;
879 Visual *vis;
880 XWindowAttributes wndattribs;
881 XVisualInfo visInfo;
882 double rRatio;
883 double gRatio;
884 double bRatio;
885 int outIndex = 0;
886 unsigned int i,j;
887 unsigned char *buf;
888 uint32_t *newBuf = 0;
889 size_t numNewBufBytes;
891 // Get visual Info
892 XGetWindowAttributes(m_dpy, m_glWindow, &wndattribs);
893 visInfo.visualid = wndattribs.visual->visualid;
894 int nvisuals = 0;
895 XVisualInfo* visuals = XGetVisualInfo(m_dpy, VisualIDMask, &visInfo, &nvisuals);
896 if (nvisuals != 1)
898 CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - could not find visual");
899 return false;
901 visInfo = visuals[0];
902 XFree(visuals);
904 depth = visInfo.depth;
905 vis = visInfo.visual;
907 if (depth < 15)
909 CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - no suitable depth");
910 return false;
913 rRatio = vis->red_mask / 255.0;
914 gRatio = vis->green_mask / 255.0;
915 bRatio = vis->blue_mask / 255.0;
917 std::unique_ptr<CTexture> iconTexture =
918 CTexture::LoadFromFile("special://xbmc/media/icon256x256.png");
920 if (!iconTexture)
921 return false;
923 buf = iconTexture->GetPixels();
925 if (depth>=24)
926 numNewBufBytes = (4 * (iconTexture->GetWidth() * iconTexture->GetHeight()));
927 else
928 numNewBufBytes = (2 * (iconTexture->GetWidth() * iconTexture->GetHeight()));
930 newBuf = (uint32_t*)malloc(numNewBufBytes);
931 if (!newBuf)
933 CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - malloc failed");
934 return false;
937 for (i=0; i<iconTexture->GetHeight();++i)
939 for (j=0; j<iconTexture->GetWidth();++j)
941 unsigned int pos = i*iconTexture->GetPitch()+j*4;
942 unsigned int r, g, b;
943 r = (buf[pos+2] * rRatio);
944 g = (buf[pos+1] * gRatio);
945 b = (buf[pos+0] * bRatio);
946 r &= vis->red_mask;
947 g &= vis->green_mask;
948 b &= vis->blue_mask;
949 newBuf[outIndex] = r | g | b;
950 ++outIndex;
953 img = XCreateImage(m_dpy, vis, depth,ZPixmap, 0, (char *)newBuf,
954 iconTexture->GetWidth(), iconTexture->GetHeight(),
955 (depth>=24)?32:16, 0);
956 if (!img)
958 CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - could not create image");
959 free(newBuf);
960 return false;
962 if (!XInitImage(img))
964 CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - init image failed");
965 XDestroyImage(img);
966 return false;
969 // set byte order
970 union
972 char c[sizeof(short)];
973 short s;
974 } order;
975 order.s = 1;
976 if ((1 == order.c[0]))
978 img->byte_order = LSBFirst;
980 else
982 img->byte_order = MSBFirst;
985 // create icon pixmap from image
986 m_icon = XCreatePixmap(m_dpy, m_glWindow, img->width, img->height, depth);
987 GC gc = XCreateGC(m_dpy, m_glWindow, 0, NULL);
988 XPutImage(m_dpy, m_icon, gc, img, 0, 0, 0, 0, img->width, img->height);
989 XFreeGC(m_dpy, gc);
990 XDestroyImage(img); // this also frees newBuf
992 return true;
995 bool CWinSystemX11::HasWindowManager()
997 Window wm_check;
998 unsigned char *data;
999 int status, real_format;
1000 Atom real_type, prop;
1001 unsigned long items_read, items_left;
1003 prop = XInternAtom(m_dpy, "_NET_SUPPORTING_WM_CHECK", True);
1004 if (prop == None)
1005 return false;
1006 status = XGetWindowProperty(m_dpy, DefaultRootWindow(m_dpy), prop,
1007 0L, 1L, False, XA_WINDOW, &real_type, &real_format,
1008 &items_read, &items_left, &data);
1009 if(status != Success || ! items_read)
1011 if(status == Success)
1012 XFree(data);
1013 return false;
1016 wm_check = ((Window*)data)[0];
1017 XFree(data);
1019 status = XGetWindowProperty(m_dpy, wm_check, prop,
1020 0L, 1L, False, XA_WINDOW, &real_type, &real_format,
1021 &items_read, &items_left, &data);
1023 if(status != Success || !items_read)
1025 if(status == Success)
1026 XFree(data);
1027 return false;
1030 if(wm_check != ((Window*)data)[0])
1032 XFree(data);
1033 return false;
1036 XFree(data);
1038 prop = XInternAtom(m_dpy, "_NET_WM_NAME", True);
1039 if (prop == None)
1041 CLog::Log(LOGDEBUG,"Window Manager Name: ");
1042 return true;
1045 status = XGetWindowProperty(m_dpy, wm_check, prop,
1046 0L, (~0L), False, AnyPropertyType, &real_type, &real_format,
1047 &items_read, &items_left, &data);
1049 if(status == Success && items_read)
1051 const char* s;
1053 s = reinterpret_cast<const char*>(data);
1054 CLog::Log(LOGDEBUG, "Window Manager Name: {}", s);
1056 else
1057 CLog::Log(LOGDEBUG,"Window Manager Name: ");
1059 if(status == Success)
1060 XFree(data);
1062 return true;
1065 void CWinSystemX11::UpdateCrtc()
1067 XWindowAttributes winattr;
1068 int posx, posy;
1069 float fps = 0.0f;
1070 Window child;
1071 XGetWindowAttributes(m_dpy, m_mainWindow, &winattr);
1072 XTranslateCoordinates(m_dpy, m_mainWindow, RootWindow(m_dpy, m_screen), winattr.x, winattr.y,
1073 &posx, &posy, &child);
1075 m_crtc = g_xrandr.GetCrtc(posx+winattr.width/2, posy+winattr.height/2, fps);
1076 CServiceBroker::GetWinSystem()->GetGfxContext().SetFPS(fps);
1079 bool CWinSystemX11::MessagePump()
1081 return m_winEvents->MessagePump();