342 lines
11 KiB
C++
342 lines
11 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library.
|
|
Copyright (c) 2017 - ROLI Ltd.
|
|
|
|
JUCE is an open source library subject to commercial or open-source
|
|
licensing.
|
|
|
|
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
|
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
|
27th April 2017).
|
|
|
|
End User License Agreement: www.juce.com/juce-5-licence
|
|
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
|
|
|
Or: You may also use this code under the terms of the GPL v3 (see
|
|
www.gnu.org/licenses).
|
|
|
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
|
DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
namespace juce
|
|
{
|
|
|
|
typedef void (*WindowMessageReceiveCallback) (XEvent&);
|
|
WindowMessageReceiveCallback dispatchWindowMessage = nullptr;
|
|
|
|
typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&);
|
|
SelectionRequestCallback handleSelectionRequest = nullptr;
|
|
|
|
::Window juce_messageWindowHandle;
|
|
XContext windowHandleXContext;
|
|
|
|
//==============================================================================
|
|
namespace X11ErrorHandling
|
|
{
|
|
static XErrorHandler oldErrorHandler = {};
|
|
static XIOErrorHandler oldIOErrorHandler = {};
|
|
|
|
//==============================================================================
|
|
// Usually happens when client-server connection is broken
|
|
int ioErrorHandler (::Display*)
|
|
{
|
|
DBG ("ERROR: connection to X server broken.. terminating.");
|
|
|
|
if (JUCEApplicationBase::isStandaloneApp())
|
|
MessageManager::getInstance()->stopDispatchLoop();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int errorHandler (::Display* display, XErrorEvent* event)
|
|
{
|
|
ignoreUnused (display, event);
|
|
|
|
#if JUCE_DEBUG_XERRORS
|
|
char errorStr[64] = { 0 };
|
|
char requestStr[64] = { 0 };
|
|
|
|
XGetErrorText (display, event->error_code, errorStr, 64);
|
|
XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64);
|
|
DBG ("ERROR: X returned " << errorStr << " for operation " << requestStr);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
void installXErrorHandlers()
|
|
{
|
|
oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler);
|
|
oldErrorHandler = XSetErrorHandler (errorHandler);
|
|
}
|
|
|
|
void removeXErrorHandlers()
|
|
{
|
|
XSetIOErrorHandler (oldIOErrorHandler);
|
|
oldIOErrorHandler = {};
|
|
|
|
XSetErrorHandler (oldErrorHandler);
|
|
oldErrorHandler = {};
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
XWindowSystem::XWindowSystem() noexcept
|
|
{
|
|
if (JUCEApplicationBase::isStandaloneApp())
|
|
{
|
|
// Initialise xlib for multiple thread support
|
|
static bool initThreadCalled = false;
|
|
|
|
if (! initThreadCalled)
|
|
{
|
|
if (! XInitThreads())
|
|
{
|
|
// This is fatal! Print error and closedown
|
|
Logger::outputDebugString ("Failed to initialise xlib thread support.");
|
|
Process::terminate();
|
|
return;
|
|
}
|
|
|
|
initThreadCalled = true;
|
|
}
|
|
|
|
X11ErrorHandling::installXErrorHandlers();
|
|
}
|
|
}
|
|
|
|
XWindowSystem::~XWindowSystem() noexcept
|
|
{
|
|
if (JUCEApplicationBase::isStandaloneApp())
|
|
X11ErrorHandling::removeXErrorHandlers();
|
|
|
|
clearSingletonInstance();
|
|
}
|
|
|
|
::Display* XWindowSystem::displayRef() noexcept
|
|
{
|
|
if (++displayCount == 1)
|
|
{
|
|
jassert (display == nullptr);
|
|
|
|
String displayName (getenv ("DISPLAY"));
|
|
|
|
if (displayName.isEmpty())
|
|
displayName = ":0.0";
|
|
|
|
// it seems that on some systems XOpenDisplay will occasionally
|
|
// fail the first time, but succeed on a second attempt..
|
|
for (int retries = 2; --retries >= 0;)
|
|
{
|
|
display = XOpenDisplay (displayName.toUTF8());
|
|
|
|
if (display != nullptr)
|
|
break;
|
|
}
|
|
|
|
initialiseXDisplay();
|
|
}
|
|
|
|
return display;
|
|
}
|
|
|
|
::Display* XWindowSystem::displayUnref() noexcept
|
|
{
|
|
jassert (display != nullptr);
|
|
jassert (displayCount.get() > 0);
|
|
|
|
if (--displayCount == 0)
|
|
{
|
|
destroyXDisplay();
|
|
XCloseDisplay (display);
|
|
display = nullptr;
|
|
}
|
|
|
|
return display;
|
|
}
|
|
|
|
void XWindowSystem::initialiseXDisplay() noexcept
|
|
{
|
|
// This is fatal! Print error and closedown
|
|
if (display == nullptr)
|
|
{
|
|
Logger::outputDebugString ("Failed to connect to the X Server.");
|
|
Process::terminate();
|
|
}
|
|
|
|
// Create a context to store user data associated with Windows we create
|
|
windowHandleXContext = XUniqueContext();
|
|
|
|
// We're only interested in client messages for this window, which are always sent
|
|
XSetWindowAttributes swa;
|
|
swa.event_mask = NoEventMask;
|
|
|
|
// Create our message window (this will never be mapped)
|
|
const int screen = DefaultScreen (display);
|
|
juce_messageWindowHandle = XCreateWindow (display, RootWindow (display, screen),
|
|
0, 0, 1, 1, 0, 0, InputOnly,
|
|
DefaultVisual (display, screen),
|
|
CWEventMask, &swa);
|
|
|
|
XSync (display, False);
|
|
|
|
// Setup input event handler
|
|
int fd = XConnectionNumber (display);
|
|
|
|
LinuxEventLoop::setWindowSystemFd (fd,
|
|
[this](int /*fd*/)
|
|
{
|
|
do
|
|
{
|
|
XEvent evt;
|
|
|
|
{
|
|
ScopedXLock xlock (display);
|
|
|
|
if (! XPending (display))
|
|
return false;
|
|
|
|
XNextEvent (display, &evt);
|
|
}
|
|
|
|
if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle
|
|
&& handleSelectionRequest != nullptr)
|
|
{
|
|
handleSelectionRequest (evt.xselectionrequest);
|
|
}
|
|
else if (evt.xany.window != juce_messageWindowHandle
|
|
&& dispatchWindowMessage != nullptr)
|
|
{
|
|
dispatchWindowMessage (evt);
|
|
}
|
|
|
|
} while (display != nullptr);
|
|
|
|
return false;
|
|
});
|
|
}
|
|
|
|
void XWindowSystem::destroyXDisplay() noexcept
|
|
{
|
|
ScopedXLock xlock (display);
|
|
XDestroyWindow (display, juce_messageWindowHandle);
|
|
juce_messageWindowHandle = 0;
|
|
XSync (display, True);
|
|
LinuxEventLoop::removeWindowSystemFd();
|
|
}
|
|
|
|
JUCE_IMPLEMENT_SINGLETON (XWindowSystem)
|
|
|
|
//==============================================================================
|
|
ScopedXDisplay::ScopedXDisplay() : display (XWindowSystem::getInstance()->displayRef())
|
|
{
|
|
}
|
|
|
|
ScopedXDisplay::~ScopedXDisplay()
|
|
{
|
|
XWindowSystem::getInstance()->displayUnref();
|
|
}
|
|
|
|
//==============================================================================
|
|
ScopedXLock::ScopedXLock (::Display* d) : display (d)
|
|
{
|
|
if (display != nullptr)
|
|
XLockDisplay (display);
|
|
}
|
|
|
|
ScopedXLock::~ScopedXLock()
|
|
{
|
|
if (display != nullptr)
|
|
XUnlockDisplay (display);
|
|
}
|
|
|
|
//==============================================================================
|
|
Atoms::Atoms (::Display* display)
|
|
{
|
|
protocols = getIfExists (display, "WM_PROTOCOLS");
|
|
protocolList [TAKE_FOCUS] = getIfExists (display, "WM_TAKE_FOCUS");
|
|
protocolList [DELETE_WINDOW] = getIfExists (display, "WM_DELETE_WINDOW");
|
|
protocolList [PING] = getIfExists (display, "_NET_WM_PING");
|
|
changeState = getIfExists (display, "WM_CHANGE_STATE");
|
|
state = getIfExists (display, "WM_STATE");
|
|
userTime = getCreating (display, "_NET_WM_USER_TIME");
|
|
activeWin = getCreating (display, "_NET_ACTIVE_WINDOW");
|
|
pid = getCreating (display, "_NET_WM_PID");
|
|
windowType = getIfExists (display, "_NET_WM_WINDOW_TYPE");
|
|
windowState = getIfExists (display, "_NET_WM_STATE");
|
|
|
|
XdndAware = getCreating (display, "XdndAware");
|
|
XdndEnter = getCreating (display, "XdndEnter");
|
|
XdndLeave = getCreating (display, "XdndLeave");
|
|
XdndPosition = getCreating (display, "XdndPosition");
|
|
XdndStatus = getCreating (display, "XdndStatus");
|
|
XdndDrop = getCreating (display, "XdndDrop");
|
|
XdndFinished = getCreating (display, "XdndFinished");
|
|
XdndSelection = getCreating (display, "XdndSelection");
|
|
|
|
XdndTypeList = getCreating (display, "XdndTypeList");
|
|
XdndActionList = getCreating (display, "XdndActionList");
|
|
XdndActionCopy = getCreating (display, "XdndActionCopy");
|
|
XdndActionPrivate = getCreating (display, "XdndActionPrivate");
|
|
XdndActionDescription = getCreating (display, "XdndActionDescription");
|
|
|
|
XembedMsgType = getCreating (display, "_XEMBED");
|
|
XembedInfo = getCreating (display, "_XEMBED_INFO");
|
|
|
|
allowedMimeTypes[0] = getCreating (display, "UTF8_STRING");
|
|
allowedMimeTypes[1] = getCreating (display, "text/plain;charset=utf-8");
|
|
allowedMimeTypes[2] = getCreating (display, "text/plain");
|
|
allowedMimeTypes[3] = getCreating (display, "text/uri-list");
|
|
|
|
allowedActions[0] = getCreating (display, "XdndActionMove");
|
|
allowedActions[1] = XdndActionCopy;
|
|
allowedActions[2] = getCreating (display, "XdndActionLink");
|
|
allowedActions[3] = getCreating (display, "XdndActionAsk");
|
|
allowedActions[4] = XdndActionPrivate;
|
|
}
|
|
|
|
Atom Atoms::getIfExists (::Display* display, const char* name) { return XInternAtom (display, name, True); }
|
|
Atom Atoms::getCreating (::Display* display, const char* name) { return XInternAtom (display, name, False); }
|
|
|
|
String Atoms::getName (::Display* display, const Atom atom)
|
|
{
|
|
if (atom == None)
|
|
return "None";
|
|
|
|
return String (XGetAtomName (display, atom));
|
|
}
|
|
|
|
bool Atoms::isMimeTypeFile (::Display* display, const Atom atom)
|
|
{
|
|
return getName (display, atom).equalsIgnoreCase ("text/uri-list");
|
|
}
|
|
|
|
|
|
const unsigned long Atoms::DndVersion = 3;
|
|
|
|
//==============================================================================
|
|
GetXProperty::GetXProperty (::Display* display, Window window, Atom atom,
|
|
long offset, long length, bool shouldDelete,
|
|
Atom requestedType)
|
|
{
|
|
success = (XGetWindowProperty (display, window, atom, offset, length,
|
|
(Bool) shouldDelete, requestedType, &actualType,
|
|
&actualFormat, &numItems, &bytesLeft, &data) == Success)
|
|
&& data != nullptr;
|
|
}
|
|
|
|
GetXProperty::~GetXProperty()
|
|
{
|
|
if (data != nullptr)
|
|
XFree (data);
|
|
}
|
|
|
|
} // namespace juce
|