openbox/src/BaseDisplay.cc

516 lines
15 KiB
C++
Raw Normal View History

// -*- mode: C++; indent-tabs-mode: nil; -*-
// BaseDisplay.cc for Blackbox - an X11 Window manager
// Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
2002-04-11 03:20:38 +00:00
// Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#ifdef HAVE_CONFIG_H
# include "../config.h"
#endif // HAVE_CONFIG_H
extern "C" {
2002-04-11 03:20:38 +00:00
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#ifdef SHAPE
# include <X11/extensions/shape.h>
#endif // SHAPE
#ifdef XINERAMA
# include <X11/extensions/Xinerama.h>
#endif // XINERAMA
2002-04-11 03:20:38 +00:00
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif // HAVE_FCNTL_H
#ifdef HAVE_STDIO_H
# include <stdio.h>
#endif // HAVE_STDIO_H
#ifdef HAVE_STDLIB_H
2002-04-11 03:20:38 +00:00
# include <stdlib.h>
#endif // HAVE_STDLIB_H
#ifdef HAVE_STRING_H
2002-04-11 03:20:38 +00:00
# include <string.h>
#endif // HAVE_STRING_H
2002-04-11 03:20:38 +00:00
#ifdef HAVE_UNISTD_H
# include <sys/types.h>
# include <unistd.h>
#endif // HAVE_UNISTD_H
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif // HAVE_SYS_SELECT_H
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif // HAVE_SIGNAL_H
#ifndef SA_NODEFER
# ifdef SA_INTERRUPT
# define SA_NODEFER SA_INTERRUPT
# else // !SA_INTERRUPT
# define SA_NODEFER (0)
# endif // SA_INTERRUPT
#endif // SA_NODEFER
#ifdef HAVE_SYS_WAIT_H
# include <sys/types.h>
# include <sys/wait.h>
#endif // HAVE_SYS_WAIT_H
}
2002-04-11 03:20:38 +00:00
#include <string>
using std::string;
2002-04-11 03:20:38 +00:00
#include "i18n.hh"
#include "BaseDisplay.hh"
#include "GCCache.hh"
#include "Timer.hh"
#include "Util.hh"
2002-04-11 03:20:38 +00:00
2002-04-11 03:20:38 +00:00
// X error handler to handle any and all X errors while the application is
// running
static bool internal_error = False;
2002-04-11 03:20:38 +00:00
BaseDisplay *base_display;
static int handleXErrors(Display *d, XErrorEvent *e) {
2002-05-30 04:35:22 +00:00
#ifdef DEBUG
2002-04-11 03:20:38 +00:00
char errtxt[128];
XGetErrorText(d, e->error_code, errtxt, 128);
fprintf(stderr,
i18n(BaseDisplaySet, BaseDisplayXError,
"%s: X error: %s(%d) opcodes %d/%d\n resource 0x%lx\n"),
2002-04-11 03:20:38 +00:00
base_display->getApplicationName(), errtxt, e->error_code,
e->request_code, e->minor_code, e->resourceid);
#else
2002-05-30 04:35:22 +00:00
// shutup gcc
(void) d;
(void) e;
2002-04-11 03:20:38 +00:00
#endif // DEBUG
if (internal_error) abort();
return(False);
}
// signal handler to allow for proper and gentle shutdown
#ifndef HAVE_SIGACTION
static RETSIGTYPE signalhandler(int sig) {
2002-04-11 03:20:38 +00:00
#else // HAVE_SIGACTION
static void signalhandler(int sig) {
2002-04-11 03:20:38 +00:00
#endif // HAVE_SIGACTION
static int re_enter = 0;
2002-04-11 03:20:38 +00:00
switch (sig) {
case SIGCHLD:
int status;
waitpid(-1, &status, WNOHANG | WUNTRACED);
2002-04-11 03:20:38 +00:00
#ifndef HAVE_SIGACTION
// assume broken, braindead sysv signal semantics
signal(SIGCHLD, (RETSIGTYPE (*)(int)) signalhandler);
2002-04-11 03:20:38 +00:00
#endif // HAVE_SIGACTION
break;
2002-04-11 03:20:38 +00:00
default:
if (base_display->handleSignal(sig)) {
2002-04-11 03:20:38 +00:00
#ifndef HAVE_SIGACTION
// assume broken, braindead sysv signal semantics
signal(sig, (RETSIGTYPE (*)(int)) signalhandler);
2002-04-11 03:20:38 +00:00
#endif // HAVE_SIGACTION
return;
}
2002-04-11 03:20:38 +00:00
fprintf(stderr, i18n(BaseDisplaySet, BaseDisplaySignalCaught,
"%s: signal %d caught\n"),
base_display->getApplicationName(), sig);
2002-04-11 03:20:38 +00:00
if (! base_display->isStartup() && ! re_enter) {
internal_error = True;
2002-04-11 03:20:38 +00:00
re_enter = 1;
fprintf(stderr, i18n(BaseDisplaySet, BaseDisplayShuttingDown,
"shutting down\n"));
base_display->shutdown();
}
2002-04-11 03:20:38 +00:00
if (sig != SIGTERM && sig != SIGINT) {
fprintf(stderr, i18n(BaseDisplaySet, BaseDisplayAborting,
"aborting... dumping core\n"));
abort();
}
2002-04-11 03:20:38 +00:00
exit(0);
2002-04-11 03:20:38 +00:00
break;
2002-04-11 03:20:38 +00:00
}
}
2002-04-11 03:20:38 +00:00
BaseDisplay::BaseDisplay(const char *app_name, const char *dpy_name) {
application_name = app_name;
2002-04-11 03:20:38 +00:00
run_state = STARTUP;
2002-04-11 03:20:38 +00:00
::base_display = this;
2002-04-11 03:20:38 +00:00
#ifdef HAVE_SIGACTION
struct sigaction action;
action.sa_handler = signalhandler;
action.sa_mask = sigset_t();
action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
sigaction(SIGPIPE, &action, NULL);
sigaction(SIGSEGV, &action, NULL);
sigaction(SIGFPE, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGINT, &action, NULL);
sigaction(SIGCHLD, &action, NULL);
sigaction(SIGHUP, &action, NULL);
sigaction(SIGUSR1, &action, NULL);
sigaction(SIGUSR2, &action, NULL);
2002-04-11 03:20:38 +00:00
#else // !HAVE_SIGACTION
signal(SIGPIPE, (RETSIGTYPE (*)(int)) signalhandler);
signal(SIGSEGV, (RETSIGTYPE (*)(int)) signalhandler);
signal(SIGFPE, (RETSIGTYPE (*)(int)) signalhandler);
signal(SIGTERM, (RETSIGTYPE (*)(int)) signalhandler);
signal(SIGINT, (RETSIGTYPE (*)(int)) signalhandler);
signal(SIGUSR1, (RETSIGTYPE (*)(int)) signalhandler);
signal(SIGUSR2, (RETSIGTYPE (*)(int)) signalhandler);
signal(SIGHUP, (RETSIGTYPE (*)(int)) signalhandler);
signal(SIGCHLD, (RETSIGTYPE (*)(int)) signalhandler);
2002-04-11 03:20:38 +00:00
#endif // HAVE_SIGACTION
if (! (display = XOpenDisplay(dpy_name))) {
fprintf(stderr,
i18n(BaseDisplaySet, BaseDisplayXConnectFail,
"BaseDisplay::BaseDisplay: connection to X server failed.\n"));
::exit(2);
} else if (fcntl(ConnectionNumber(display), F_SETFD, 1) == -1) {
fprintf(stderr,
i18n(BaseDisplaySet, BaseDisplayCloseOnExecFail,
"BaseDisplay::BaseDisplay: couldn't mark display connection "
"as close-on-exec\n"));
::exit(2);
}
2002-04-11 03:20:38 +00:00
display_name = XDisplayName(dpy_name);
2002-04-11 03:20:38 +00:00
#ifdef SHAPE
shape.extensions = XShapeQueryExtension(display, &shape.event_basep,
&shape.error_basep);
2002-04-11 03:20:38 +00:00
#else // !SHAPE
shape.extensions = False;
2002-04-11 03:20:38 +00:00
#endif // SHAPE
xinerama.extensions = False;
#ifdef XINERAMA
if (XineramaQueryExtension(display, &xinerama.event_basep,
&xinerama.error_basep)) {
if (XineramaQueryVersion(display, &xinerama.major,
&xinerama.minor)) {
#ifdef DEBUG
fprintf(stderr,
"BaseDisplay::BaseDisplay: Found Xinerama version %d.%d\n",
xinerama.major, xinerama.minor);
#endif // DEBUG
xinerama.extensions = True;
}
}
#endif // XINERAMA
XSetErrorHandler((XErrorHandler) handleXErrors);
2002-04-11 03:20:38 +00:00
screenInfoList.reserve(ScreenCount(display));
for (int i = 0; i < ScreenCount(display); ++i)
screenInfoList.push_back(ScreenInfo(this, i));
2002-04-11 03:20:38 +00:00
NumLockMask = ScrollLockMask = 0;
const XModifierKeymap* const modmap = XGetModifierMapping(display);
if (modmap && modmap->max_keypermod > 0) {
const int mask_table[] = {
ShiftMask, LockMask, ControlMask, Mod1Mask,
Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
};
const size_t size = (sizeof(mask_table) / sizeof(mask_table[0])) *
modmap->max_keypermod;
// get the values of the keyboard lock modifiers
// Note: Caps lock is not retrieved the same way as Scroll and Num lock
// since it doesn't need to be.
const KeyCode num_lock = XKeysymToKeycode(display, XK_Num_Lock);
const KeyCode scroll_lock = XKeysymToKeycode(display, XK_Scroll_Lock);
for (size_t cnt = 0; cnt < size; ++cnt) {
if (! modmap->modifiermap[cnt]) continue;
if (num_lock == modmap->modifiermap[cnt])
NumLockMask = mask_table[cnt / modmap->max_keypermod];
if (scroll_lock == modmap->modifiermap[cnt])
ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
2002-04-11 03:20:38 +00:00
}
}
2002-04-11 03:20:38 +00:00
MaskList[0] = 0;
MaskList[1] = LockMask;
MaskList[2] = NumLockMask;
MaskList[3] = LockMask | NumLockMask;
MaskList[4] = ScrollLockMask;
MaskList[5] = ScrollLockMask | LockMask;
MaskList[6] = ScrollLockMask | NumLockMask;
MaskList[7] = ScrollLockMask | LockMask | NumLockMask;
MaskListLength = sizeof(MaskList) / sizeof(MaskList[0]);
if (modmap) XFreeModifiermap(const_cast<XModifierKeymap*>(modmap));
gccache = (BGCCache *) 0;
}
2002-04-11 03:20:38 +00:00
BaseDisplay::~BaseDisplay(void) {
delete gccache;
XCloseDisplay(display);
}
2002-04-11 03:20:38 +00:00
void BaseDisplay::eventLoop(void) {
run();
2002-04-11 03:20:38 +00:00
const int xfd = ConnectionNumber(display);
2002-04-11 03:20:38 +00:00
while (run_state == RUNNING && ! internal_error) {
if (XPending(display)) {
XEvent e;
XNextEvent(display, &e);
process_event(&e);
} else {
fd_set rfds;
timeval now, tm, *timeout = (timeval *) 0;
2002-04-11 03:20:38 +00:00
FD_ZERO(&rfds);
FD_SET(xfd, &rfds);
2002-04-11 03:20:38 +00:00
if (! timerList.empty()) {
const BTimer* const timer = timerList.top();
2002-04-11 03:20:38 +00:00
gettimeofday(&now, 0);
tm = timer->timeRemaining(now);
2002-04-11 03:20:38 +00:00
timeout = &tm;
}
select(xfd + 1, &rfds, 0, 0, timeout);
// check for timer timeout
gettimeofday(&now, 0);
// there is a small chance for deadlock here:
// *IF* the timer list keeps getting refreshed *AND* the time between
// timer->start() and timer->shouldFire() is within the timer's period
// then the timer will keep firing. This should be VERY near impossible.
while (! timerList.empty()) {
BTimer *timer = timerList.top();
if (! timer->shouldFire(now))
2002-05-23 14:27:52 +00:00
break;
timerList.pop();
2002-05-23 14:27:52 +00:00
timer->fireTimeout();
timer->halt();
if (timer->isRecurring())
2002-05-23 14:27:52 +00:00
timer->start();
2002-04-11 03:20:38 +00:00
}
}
}
}
2002-04-11 03:20:38 +00:00
void BaseDisplay::addTimer(BTimer *timer) {
if (! timer) return;
2002-04-11 03:20:38 +00:00
timerList.push(timer);
}
2002-04-11 03:20:38 +00:00
2002-05-23 14:27:52 +00:00
void BaseDisplay::removeTimer(BTimer *timer) {
timerList.release(timer);
2002-05-23 14:27:52 +00:00
}
/*
* Grabs a button, but also grabs the button in every possible combination
* with the keyboard lock keys, so that they do not cancel out the event.
* if allow_scroll_lock is true then only the top half of the lock mask
* table is used and scroll lock is ignored. This value defaults to false.
*/
void BaseDisplay::grabButton(unsigned int button, unsigned int modifiers,
Window grab_window, bool owner_events,
unsigned int event_mask, int pointer_mode,
int keyboard_mode, Window confine_to,
Cursor cursor, bool allow_scroll_lock) const {
unsigned int length = (allow_scroll_lock) ? MaskListLength / 2:
MaskListLength;
for (size_t cnt = 0; cnt < length; ++cnt)
XGrabButton(display, button, modifiers | MaskList[cnt], grab_window,
owner_events, event_mask, pointer_mode, keyboard_mode,
2002-04-11 03:20:38 +00:00
confine_to, cursor);
}
2002-04-11 03:20:38 +00:00
/*
* Releases the grab on a button, and ungrabs all possible combinations of the
* keyboard lock keys.
*/
void BaseDisplay::ungrabButton(unsigned int button, unsigned int modifiers,
Window grab_window) const {
for (size_t cnt = 0; cnt < MaskListLength; ++cnt)
XUngrabButton(display, button, modifiers | MaskList[cnt], grab_window);
}
const ScreenInfo* BaseDisplay::getScreenInfo(unsigned int s) const {
if (s < screenInfoList.size())
return &screenInfoList[s];
return (const ScreenInfo*) 0;
}
2002-04-11 03:20:38 +00:00
BGCCache* BaseDisplay::gcCache(void) const {
if (! gccache)
gccache = new BGCCache(this);
return gccache;
}
ScreenInfo::ScreenInfo(BaseDisplay *d, unsigned int num) {
basedisplay = d;
screen_number = num;
root_window = RootWindow(basedisplay->getXDisplay(), screen_number);
rect.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
screen_number)),
HeightOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
screen_number)));
2002-06-21 01:06:29 +00:00
/*
If the default depth is at least 8 we will use that,
2002-06-21 01:06:29 +00:00
otherwise we try to find the largest TrueColor visual.
Preference is given to 24 bit over larger depths if 24 bit is an option.
*/
2002-06-21 01:06:29 +00:00
depth = DefaultDepth(basedisplay->getXDisplay(), screen_number);
visual = DefaultVisual(basedisplay->getXDisplay(), screen_number);
colormap = DefaultColormap(basedisplay->getXDisplay(), screen_number);
if (depth < 8) {
2002-06-21 01:06:29 +00:00
// search for a TrueColor Visual... if we can't find one...
// we will use the default visual for the screen
XVisualInfo vinfo_template, *vinfo_return;
int vinfo_nitems;
int best = -1;
vinfo_template.screen = screen_number;
vinfo_template.c_class = TrueColor;
vinfo_return = XGetVisualInfo(basedisplay->getXDisplay(),
VisualScreenMask | VisualClassMask,
&vinfo_template, &vinfo_nitems);
if (vinfo_return) {
int max_depth = 1;
for (int i = 0; i < vinfo_nitems; ++i) {
if (vinfo_return[i].depth > max_depth) {
if (max_depth == 24 && vinfo_return[i].depth > 24)
break; // prefer 24 bit over 32
max_depth = vinfo_return[i].depth;
best = i;
}
2002-04-11 03:20:38 +00:00
}
2002-06-21 01:06:29 +00:00
if (max_depth < depth) best = -1;
}
2002-04-11 03:20:38 +00:00
2002-06-21 01:06:29 +00:00
if (best != -1) {
depth = vinfo_return[best].depth;
visual = vinfo_return[best].visual;
colormap = XCreateColormap(basedisplay->getXDisplay(), root_window,
visual, AllocNone);
}
2002-06-21 01:06:29 +00:00
XFree(vinfo_return);
2002-04-11 03:20:38 +00:00
}
// get the default display string and strip the screen number
string default_string = DisplayString(basedisplay->getXDisplay());
const string::size_type pos = default_string.rfind(".");
if (pos != string::npos)
default_string.resize(pos);
display_string = string("DISPLAY=") + default_string + '.' +
itostring(static_cast<unsigned long>(screen_number));
#ifdef XINERAMA
if (d->hasXineramaExtensions()) {
if (d->getXineramaMajorVersion() == 1) {
// we know the version 1(.1?) protocol
/*
in this version of Xinerama, we can't query on a per-screen basis, but
in future versions we should be able, so the 'activeness' is checked
on a pre-screen basis anyways.
*/
xinerama_active = XineramaIsActive(d->getXDisplay());
/*
If Xinerama is being used, there there is only going to be one screen
present. We still, of course, want to use the screen class, but that is
why no screen number is used in this function call. There should never
be more than one screen present with Xinerama active.
*/
int num;
XineramaScreenInfo *info = XineramaQueryScreens(d->getXDisplay(), &num);
if (num > 0 && info) {
for (int i = 0; i < num; ++i) {
xinerama_areas.push_back(Rect(info[i].x_org, info[i].y_org,
info[i].width, info[i].height));
}
XFree(info);
}
}
}
#endif // XINERAMA
}