303 lines
7.1 KiB
C++
303 lines
7.1 KiB
C++
/*-------------------------------------------------------------------------
|
|
* 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
|