blob: dfe09d06016c324c0e09c495ab8c78a999d2e4c5 [file] [log] [blame]
/*-------------------------------------------------------------------------
* drawElements Quality Program Tester Core
* ----------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief X11 utilities.
*//*--------------------------------------------------------------------*/
#include "tcuLnxX11.hpp"
#include "gluRenderConfig.hpp"
#include "deMemory.h"
#include <X11/Xutil.h>
namespace tcu
{
namespace lnx
{
namespace x11
{
DisplayBase::DisplayBase (EventState& platform)
: m_eventState (platform)
{
}
DisplayBase::~DisplayBase (void)
{
}
WindowBase::WindowBase ()
: m_visible (false)
{
}
WindowBase::~WindowBase (void)
{
}
XlibDisplay::DisplayState XlibDisplay::s_displayState = XlibDisplay::DISPLAY_STATE_UNKNOWN;
bool XlibDisplay::hasDisplay (const char* name)
{
if (s_displayState == DISPLAY_STATE_UNKNOWN)
{
XInitThreads();
Display *display = XOpenDisplay((char*)name);
if (display)
{
s_displayState = DISPLAY_STATE_AVAILABLE;
XCloseDisplay(display);
} else
s_displayState = DISPLAY_STATE_UNAVAILABLE;
}
return s_displayState == DISPLAY_STATE_AVAILABLE ? true : false;
}
XlibDisplay::XlibDisplay (EventState& eventState, const char* name)
: DisplayBase (eventState)
{
// From man:XinitThreads(3):
//
// The XInitThreads function initializes Xlib support for concurrent
// threads. This function must be the first Xlib function
// a multi-threaded program calls, and it must complete before any other
// Xlib call is made.
DE_CHECK_RUNTIME_ERR(XInitThreads() != 0);
m_display = XOpenDisplay((char*)name); // Won't modify argument string.
if (!m_display)
throw ResourceError("Failed to open display", name, __FILE__, __LINE__);
m_deleteAtom = XInternAtom(m_display, "WM_DELETE_WINDOW", False);
}
XlibDisplay::~XlibDisplay (void)
{
XCloseDisplay(m_display);
}
void XlibDisplay::processEvent (XEvent& event)
{
switch (event.type)
{
case ClientMessage:
if ((unsigned)event.xclient.data.l[0] == m_deleteAtom)
m_eventState.setQuitFlag(true);
break;
// note: ConfigureNotify for window is handled in setDimensions()
default:
break;
}
}
void XlibDisplay::processEvents (void)
{
XEvent event;
while (XPending(m_display))
{
XNextEvent(m_display, &event);
processEvent(event);
}
}
bool XlibDisplay::getVisualInfo (VisualID visualID, XVisualInfo& dst)
{
XVisualInfo query;
query.visualid = visualID;
int numVisuals = 0;
XVisualInfo* response = XGetVisualInfo(m_display, VisualIDMask, &query, &numVisuals);
bool succ = false;
if (response != DE_NULL)
{
if (numVisuals > 0) // should be 1, but you never know...
{
dst = response[0];
succ = true;
}
XFree(response);
}
return succ;
}
::Visual* XlibDisplay::getVisual (VisualID visualID)
{
XVisualInfo info;
if (getVisualInfo(visualID, info))
return info.visual;
return DE_NULL;
}
XlibWindow::XlibWindow (XlibDisplay& display, int width, int height, ::Visual* visual)
: WindowBase ()
, m_display (display)
, m_colormap (None)
, m_window (None)
{
XSetWindowAttributes swa;
::Display* const dpy = m_display.getXDisplay();
::Window root = DefaultRootWindow(dpy);
unsigned long mask = CWBorderPixel | CWEventMask;
// If redirect is enabled, window size can't be guaranteed and it is up to
// the window manager to decide whether to honor sizing requests. However,
// overriding that causes window to appear as an overlay, which causes
// other issues, so this is disabled by default.
const bool overrideRedirect = false;
int depth = CopyFromParent;
if (overrideRedirect)
{
mask |= CWOverrideRedirect;
swa.override_redirect = true;
}
if (visual == DE_NULL)
visual = CopyFromParent;
else
{
XVisualInfo info = XVisualInfo();
bool succ = display.getVisualInfo(XVisualIDFromVisual(visual), info);
TCU_CHECK_INTERNAL(succ);
root = RootWindow(dpy, info.screen);
m_colormap = XCreateColormap(dpy, root, visual, AllocNone);
swa.colormap = m_colormap;
mask |= CWColormap;
depth = info.depth;
}
swa.border_pixel = 0;
swa.event_mask = ExposureMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask;
if (width == glu::RenderConfig::DONT_CARE)
width = DEFAULT_WINDOW_WIDTH;
if (height == glu::RenderConfig::DONT_CARE)
height = DEFAULT_WINDOW_HEIGHT;
m_window = XCreateWindow(dpy, root, 0, 0, width, height, 0,
depth, InputOutput, visual, mask, &swa);
TCU_CHECK(m_window);
/* Prevent the window from stealing input, since our windows are
* non-interactive.
*/
XWMHints *hints = XAllocWMHints();
hints->flags |= InputHint;
hints->input = False;
XSetWMHints(dpy, m_window, hints);
XFree(hints);
Atom deleteAtom = m_display.getDeleteAtom();
XSetWMProtocols(dpy, m_window, &deleteAtom, 1);
XSync(dpy,false);
}
void XlibWindow::setVisibility (bool visible)
{
::Display* dpy = m_display.getXDisplay();
int eventType = None;
XEvent event;
if (visible == m_visible)
return;
if (visible)
{
XMapWindow(dpy, m_window);
eventType = MapNotify;
}
else
{
XUnmapWindow(dpy, m_window);
eventType = UnmapNotify;
}
// We are only interested about exposure/structure notify events, not user input
XSelectInput(dpy, m_window, ExposureMask | StructureNotifyMask);
do
{
XWindowEvent(dpy, m_window, ExposureMask | StructureNotifyMask, &event);
} while (event.type != eventType);
m_visible = visible;
}
void XlibWindow::getDimensions (int* width, int* height) const
{
int x, y;
::Window root;
unsigned width_, height_, borderWidth, depth;
XGetGeometry(m_display.getXDisplay(), m_window, &root, &x, &y, &width_, &height_, &borderWidth, &depth);
if (width != DE_NULL)
*width = static_cast<int>(width_);
if (height != DE_NULL)
*height = static_cast<int>(height_);
}
void XlibWindow::setDimensions (int width, int height)
{
const unsigned int mask = CWWidth | CWHeight;
XWindowChanges changes;
::Display* dpy = m_display.getXDisplay();
XEvent myevent;
changes.width = width;
changes.height = height;
XConfigureWindow(dpy, m_window, mask, &changes);
XFlush(dpy);
for(;;)
{
XNextEvent(dpy, &myevent);
if (myevent.type == ConfigureNotify) {
XConfigureEvent e = myevent.xconfigure;
if (e.width == width && e.height == height)
break;
}
else
m_display.processEvent(myevent);
}
}
void XlibWindow::processEvents (void)
{
// A bit of a hack, since we don't really handle all the events.
m_display.processEvents();
}
XlibWindow::~XlibWindow (void)
{
XDestroyWindow(m_display.getXDisplay(), m_window);
if (m_colormap != None)
XFreeColormap(m_display.getXDisplay(), m_colormap);
}
} // x11
} // lnx
} // tcu