2015-02-12 21:34:06 +00:00
|
|
|
/**
|
|
|
|
* rofi
|
|
|
|
*
|
|
|
|
* MIT/X11 License
|
|
|
|
* Copyright (c) 2012 Sean Pringle <sean.pringle@gmail.com>
|
2015-12-31 23:27:00 +00:00
|
|
|
* Modified 2013-2016 Qball Cow <qball@gmpclient.org>
|
2015-02-12 21:34:06 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
2015-02-09 18:35:51 +00:00
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <glib.h>
|
2015-09-27 09:46:19 +00:00
|
|
|
#include <cairo.h>
|
2015-02-09 18:35:51 +00:00
|
|
|
|
|
|
|
#include <X11/X.h>
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xmd.h>
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
#include <X11/Xproto.h>
|
|
|
|
#include <X11/keysym.h>
|
|
|
|
#include <X11/XKBlib.h>
|
|
|
|
#include <X11/extensions/Xinerama.h>
|
|
|
|
|
2015-04-06 15:13:26 +00:00
|
|
|
#include <rofi.h>
|
2015-09-19 10:21:30 +00:00
|
|
|
#define OVERLAP( a, b, c, \
|
|
|
|
d ) ( ( ( a ) == ( c ) && \
|
|
|
|
( b ) == ( d ) ) || \
|
|
|
|
MIN ( ( a ) + ( b ), ( c ) + ( d ) ) - MAX ( ( a ), ( c ) ) > 0 )
|
|
|
|
#define INTERSECT( x, y, w, h, x1, y1, w1, \
|
|
|
|
h1 ) ( OVERLAP ( ( x ), ( w ), ( x1 ), \
|
|
|
|
( w1 ) ) && OVERLAP ( ( y ), ( h ), ( y1 ), ( h1 ) ) )
|
2015-02-09 18:35:51 +00:00
|
|
|
#include "x11-helper.h"
|
|
|
|
|
2015-09-28 19:41:58 +00:00
|
|
|
Atom netatoms[NUM_NETATOMS];
|
|
|
|
const char *netatom_names[] = { EWMH_ATOMS ( ATOM_CHAR ) };
|
2015-02-09 18:35:51 +00:00
|
|
|
// Mask indicating num-lock.
|
2015-11-20 21:00:37 +00:00
|
|
|
unsigned int NumlockMask = 0;
|
|
|
|
unsigned int AltMask = 0;
|
|
|
|
unsigned int AltRMask = 0;
|
|
|
|
unsigned int SuperRMask = 0;
|
|
|
|
unsigned int SuperLMask = 0;
|
|
|
|
unsigned int HyperRMask = 0;
|
|
|
|
unsigned int HyperLMask = 0;
|
|
|
|
unsigned int MetaRMask = 0;
|
|
|
|
unsigned int MetaLMask = 0;
|
|
|
|
unsigned int CombinedMask = 0;
|
2015-09-28 19:41:58 +00:00
|
|
|
|
|
|
|
extern Colormap map;
|
2015-02-09 18:35:51 +00:00
|
|
|
|
|
|
|
// retrieve a property of any type from a window
|
2015-09-19 10:57:48 +00:00
|
|
|
int window_get_prop ( Display *display, Window w, Atom prop, Atom *type, int *items, void *buffer, unsigned int bytes )
|
2015-02-09 18:35:51 +00:00
|
|
|
{
|
|
|
|
int format;
|
|
|
|
unsigned long nitems, nbytes;
|
|
|
|
unsigned char *ret = NULL;
|
|
|
|
memset ( buffer, 0, bytes );
|
|
|
|
|
2015-09-19 18:59:50 +00:00
|
|
|
if ( XGetWindowProperty ( display, w, prop, 0, bytes / 4, False, AnyPropertyType, type, &format, &nitems, &nbytes, &ret ) == Success &&
|
|
|
|
ret && *type != None && format ) {
|
2015-02-09 18:35:51 +00:00
|
|
|
if ( format == 8 ) {
|
|
|
|
memmove ( buffer, ret, MIN ( bytes, nitems ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( format == 16 ) {
|
|
|
|
memmove ( buffer, ret, MIN ( bytes, nitems * sizeof ( short ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( format == 32 ) {
|
|
|
|
memmove ( buffer, ret, MIN ( bytes, nitems * sizeof ( long ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
*items = ( int ) nitems;
|
|
|
|
XFree ( ret );
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// retrieve a text property from a window
|
|
|
|
// technically we could use window_get_prop(), but this is better for character set support
|
|
|
|
char* window_get_text_prop ( Display *display, Window w, Atom atom )
|
|
|
|
{
|
|
|
|
XTextProperty prop;
|
|
|
|
char *res = NULL;
|
|
|
|
char **list = NULL;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
if ( XGetTextProperty ( display, w, &prop, atom ) && prop.value && prop.nitems ) {
|
|
|
|
if ( prop.encoding == XA_STRING ) {
|
2015-05-14 17:39:30 +00:00
|
|
|
size_t l = strlen ( ( char *) prop.value ) + 1;
|
|
|
|
res = g_malloc ( l );
|
2015-02-09 18:35:51 +00:00
|
|
|
// make clang-check happy.
|
|
|
|
if ( res ) {
|
2015-05-14 17:39:30 +00:00
|
|
|
g_strlcpy ( res, ( char * ) prop.value, l );
|
2015-02-09 18:35:51 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-19 10:57:48 +00:00
|
|
|
else if ( Xutf8TextPropertyToTextList ( display, &prop, &list, &count ) >= Success && count > 0 && *list ) {
|
2015-05-14 17:39:30 +00:00
|
|
|
size_t l = strlen ( *list ) + 1;
|
|
|
|
res = g_malloc ( l );
|
2015-02-09 18:35:51 +00:00
|
|
|
// make clang-check happy.
|
|
|
|
if ( res ) {
|
2015-05-14 17:39:30 +00:00
|
|
|
g_strlcpy ( res, *list, l );
|
2015-02-09 18:35:51 +00:00
|
|
|
}
|
|
|
|
XFreeStringList ( list );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( prop.value ) {
|
|
|
|
XFree ( prop.value );
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
int window_get_atom_prop ( Display *display, Window w, Atom atom, Atom *list, int count )
|
|
|
|
{
|
|
|
|
Atom type;
|
|
|
|
int items;
|
2015-09-19 10:57:48 +00:00
|
|
|
return window_get_prop ( display, w, atom, &type, &items, list, count * sizeof ( Atom ) ) && type == XA_ATOM ? items : 0;
|
2015-02-09 18:35:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void window_set_atom_prop ( Display *display, Window w, Atom prop, Atom *atoms, int count )
|
|
|
|
{
|
2015-09-19 10:57:48 +00:00
|
|
|
XChangeProperty ( display, w, prop, XA_ATOM, 32, PropModeReplace, ( unsigned char * ) atoms, count );
|
2015-02-09 18:35:51 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 10:57:48 +00:00
|
|
|
int window_get_cardinal_prop ( Display *display, Window w, Atom atom, unsigned long *list, int count )
|
2015-02-09 18:35:51 +00:00
|
|
|
{
|
|
|
|
Atom type; int items;
|
2015-09-19 10:57:48 +00:00
|
|
|
return window_get_prop ( display, w, atom, &type, &items, list, count * sizeof ( unsigned long ) ) && type == XA_CARDINAL ? items : 0;
|
2015-02-09 18:35:51 +00:00
|
|
|
}
|
2015-10-18 11:40:39 +00:00
|
|
|
int monitor_get_smallest_size ( Display *display )
|
|
|
|
{
|
|
|
|
int size = MIN ( WidthOfScreen ( DefaultScreenOfDisplay ( display ) ),
|
|
|
|
HeightOfScreen ( DefaultScreenOfDisplay ( display ) ) );
|
|
|
|
// locate the current monitor
|
|
|
|
if ( XineramaIsActive ( display ) ) {
|
|
|
|
int monitors;
|
|
|
|
XineramaScreenInfo *info = XineramaQueryScreens ( display, &monitors );
|
2015-02-09 18:35:51 +00:00
|
|
|
|
2015-10-18 11:40:39 +00:00
|
|
|
if ( info ) {
|
|
|
|
for ( int i = 0; i < monitors; i++ ) {
|
|
|
|
size = MIN ( info[i].width, size );
|
|
|
|
size = MIN ( info[i].height, size );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
XFree ( info );
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
2015-08-02 13:45:52 +00:00
|
|
|
int monitor_get_dimension ( Display *display, Screen *screen, int monitor, workarea *mon )
|
|
|
|
{
|
|
|
|
memset ( mon, 0, sizeof ( workarea ) );
|
|
|
|
mon->w = WidthOfScreen ( screen );
|
|
|
|
mon->h = HeightOfScreen ( screen );
|
|
|
|
// locate the current monitor
|
|
|
|
if ( XineramaIsActive ( display ) ) {
|
|
|
|
int monitors;
|
|
|
|
XineramaScreenInfo *info = XineramaQueryScreens ( display, &monitors );
|
|
|
|
|
|
|
|
if ( info ) {
|
|
|
|
if ( monitor >= 0 && monitor < monitors ) {
|
|
|
|
mon->x = info[monitor].x_org;
|
|
|
|
mon->y = info[monitor].y_org;
|
|
|
|
mon->w = info[monitor].width;
|
|
|
|
mon->h = info[monitor].height;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
XFree ( info );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
2015-02-09 18:35:51 +00:00
|
|
|
// find the dimensions of the monitor displaying point x,y
|
2015-02-12 21:16:32 +00:00
|
|
|
void monitor_dimensions ( Display *display, Screen *screen, int x, int y, workarea *mon )
|
2015-02-09 18:35:51 +00:00
|
|
|
{
|
|
|
|
memset ( mon, 0, sizeof ( workarea ) );
|
|
|
|
mon->w = WidthOfScreen ( screen );
|
|
|
|
mon->h = HeightOfScreen ( screen );
|
|
|
|
|
|
|
|
// locate the current monitor
|
|
|
|
if ( XineramaIsActive ( display ) ) {
|
|
|
|
int monitors;
|
|
|
|
XineramaScreenInfo *info = XineramaQueryScreens ( display, &monitors );
|
|
|
|
|
|
|
|
if ( info ) {
|
|
|
|
for ( int i = 0; i < monitors; i++ ) {
|
2015-09-19 10:57:48 +00:00
|
|
|
if ( INTERSECT ( x, y, 1, 1, info[i].x_org, info[i].y_org, info[i].width, info[i].height ) ) {
|
2015-02-09 18:35:51 +00:00
|
|
|
mon->x = info[i].x_org;
|
|
|
|
mon->y = info[i].y_org;
|
|
|
|
mon->w = info[i].width;
|
|
|
|
mon->h = info[i].height;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
XFree ( info );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param x The x position of the mouse [out]
|
|
|
|
* @param y The y position of the mouse [out]
|
|
|
|
*
|
|
|
|
* find mouse pointer location
|
|
|
|
*
|
|
|
|
* @returns 1 when found
|
|
|
|
*/
|
|
|
|
static int pointer_get ( Display *display, Window root, int *x, int *y )
|
|
|
|
{
|
|
|
|
*x = 0;
|
|
|
|
*y = 0;
|
|
|
|
Window rr, cr;
|
|
|
|
int rxr, ryr, wxr, wyr;
|
|
|
|
unsigned int mr;
|
|
|
|
|
|
|
|
if ( XQueryPointer ( display, root, &rr, &cr, &rxr, &ryr, &wxr, &wyr, &mr ) ) {
|
|
|
|
*x = rxr;
|
|
|
|
*y = ryr;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine which monitor holds the active window, or failing that the mouse pointer
|
|
|
|
void monitor_active ( Display *display, workarea *mon )
|
|
|
|
{
|
|
|
|
Screen *screen = DefaultScreenOfDisplay ( display );
|
|
|
|
Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) );
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
Window id;
|
|
|
|
Atom type;
|
|
|
|
int count;
|
2015-08-02 13:45:52 +00:00
|
|
|
if ( config.monitor >= 0 ) {
|
|
|
|
if ( monitor_get_dimension ( display, screen, config.monitor, mon ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fprintf ( stderr, "Failed to find selected monitor.\n" );
|
|
|
|
}
|
2015-09-19 18:59:50 +00:00
|
|
|
if ( window_get_prop ( display, root, netatoms[_NET_ACTIVE_WINDOW], &type, &count, &id, sizeof ( Window ) )
|
|
|
|
&& type == XA_WINDOW && count > 0 ) {
|
2015-02-12 21:16:32 +00:00
|
|
|
XWindowAttributes attr;
|
|
|
|
if ( XGetWindowAttributes ( display, id, &attr ) ) {
|
2015-02-09 18:35:51 +00:00
|
|
|
Window junkwin;
|
2015-09-19 10:57:48 +00:00
|
|
|
if ( XTranslateCoordinates ( display, id, attr.root, -attr.border_width, -attr.border_width, &x, &y, &junkwin ) == True ) {
|
2015-08-17 16:32:17 +00:00
|
|
|
if ( config.monitor == -2 ) {
|
|
|
|
// place the menu above the window
|
|
|
|
// if some window is focused, place menu above window, else fall
|
|
|
|
// back to selected monitor.
|
|
|
|
mon->x = x;
|
|
|
|
mon->y = y;
|
|
|
|
mon->w = attr.width;
|
|
|
|
mon->h = attr.height;
|
|
|
|
mon->t = attr.border_width;
|
|
|
|
mon->b = attr.border_width;
|
|
|
|
mon->l = attr.border_width;
|
|
|
|
mon->r = attr.border_width;
|
|
|
|
return;
|
|
|
|
}
|
2015-02-09 18:35:51 +00:00
|
|
|
monitor_dimensions ( display, screen, x, y, mon );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( pointer_get ( display, root, &x, &y ) ) {
|
|
|
|
monitor_dimensions ( display, screen, x, y, mon );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
monitor_dimensions ( display, screen, 0, 0, mon );
|
|
|
|
}
|
|
|
|
|
2015-09-19 10:57:48 +00:00
|
|
|
int window_send_message ( Display *display, Window trg, Window subject, Atom atom, unsigned long protocol, unsigned long mask, Time time )
|
2015-02-09 18:35:51 +00:00
|
|
|
{
|
|
|
|
XEvent e;
|
|
|
|
memset ( &e, 0, sizeof ( XEvent ) );
|
|
|
|
e.xclient.type = ClientMessage;
|
|
|
|
e.xclient.message_type = atom;
|
|
|
|
e.xclient.window = subject;
|
|
|
|
e.xclient.data.l[0] = protocol;
|
|
|
|
e.xclient.data.l[1] = time;
|
|
|
|
e.xclient.send_event = True;
|
|
|
|
e.xclient.format = 32;
|
2015-09-19 10:57:48 +00:00
|
|
|
int r = XSendEvent ( display, trg, False, mask, &e ) ? 1 : 0;
|
2015-02-09 18:35:51 +00:00
|
|
|
XFlush ( display );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-09-16 19:01:40 +00:00
|
|
|
extern unsigned int normal_window_mode;
|
2015-02-09 18:35:51 +00:00
|
|
|
int take_keyboard ( Display *display, Window w )
|
|
|
|
{
|
2015-09-16 19:01:40 +00:00
|
|
|
if ( normal_window_mode ) {
|
|
|
|
return 1;
|
|
|
|
}
|
2015-05-03 11:04:03 +00:00
|
|
|
for ( int i = 0; i < 500; i++ ) {
|
2015-09-19 10:21:30 +00:00
|
|
|
if ( XGrabKeyboard ( display, w, True, GrabModeAsync, GrabModeAsync,
|
|
|
|
CurrentTime ) == GrabSuccess ) {
|
2015-05-02 10:42:36 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
usleep ( 1000 );
|
2015-02-09 18:35:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void release_keyboard ( Display *display )
|
|
|
|
{
|
2015-09-16 19:01:40 +00:00
|
|
|
if ( !normal_window_mode ) {
|
|
|
|
XUngrabKeyboard ( display, CurrentTime );
|
|
|
|
}
|
2015-02-09 18:35:51 +00:00
|
|
|
}
|
|
|
|
// bind a key combination on a root window, compensating for Lock* states
|
2015-02-09 19:05:30 +00:00
|
|
|
void x11_grab_key ( Display *display, unsigned int modmask, KeySym key )
|
2015-02-09 18:35:51 +00:00
|
|
|
{
|
|
|
|
Screen *screen = DefaultScreenOfDisplay ( display );
|
|
|
|
Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) );
|
|
|
|
KeyCode keycode = XKeysymToKeycode ( display, key );
|
|
|
|
|
2015-02-16 01:03:26 +00:00
|
|
|
// bind to combinations of mod and lock masks, so caps and numlock don't confuse people
|
|
|
|
XGrabKey ( display, keycode, modmask, root, True, GrabModeAsync, GrabModeAsync );
|
|
|
|
XGrabKey ( display, keycode, modmask | LockMask, root, True, GrabModeAsync, GrabModeAsync );
|
2015-02-09 18:35:51 +00:00
|
|
|
|
2015-02-16 01:03:26 +00:00
|
|
|
if ( NumlockMask ) {
|
2015-09-19 10:57:48 +00:00
|
|
|
XGrabKey ( display, keycode, modmask | NumlockMask, root, True, GrabModeAsync, GrabModeAsync );
|
|
|
|
XGrabKey ( display, keycode, modmask | NumlockMask | LockMask, root, True, GrabModeAsync, GrabModeAsync );
|
2015-02-09 18:35:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-29 07:37:40 +00:00
|
|
|
void x11_ungrab_key ( Display *display, unsigned int modmask, KeySym key )
|
|
|
|
{
|
|
|
|
Screen *screen = DefaultScreenOfDisplay ( display );
|
|
|
|
Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) );
|
|
|
|
KeyCode keycode = XKeysymToKeycode ( display, key );
|
|
|
|
|
|
|
|
// unbind to combinations of mod and lock masks, so caps and numlock don't confuse people
|
|
|
|
XUngrabKey ( display, keycode, modmask, root );
|
|
|
|
XUngrabKey ( display, keycode, modmask | LockMask, root );
|
|
|
|
|
|
|
|
if ( NumlockMask ) {
|
|
|
|
XUngrabKey ( display, keycode, modmask | NumlockMask, root );
|
|
|
|
XUngrabKey ( display, keycode, modmask | NumlockMask | LockMask, root );
|
|
|
|
}
|
|
|
|
}
|
2015-02-10 07:12:03 +00:00
|
|
|
/**
|
|
|
|
* @param display The connection to the X server.
|
|
|
|
*
|
|
|
|
* Figure out what entry in the modifiermap is NumLock.
|
|
|
|
* This sets global variable: NumlockMask
|
|
|
|
*/
|
2015-02-09 18:35:51 +00:00
|
|
|
static void x11_figure_out_numlock_mask ( Display *display )
|
|
|
|
{
|
2015-11-20 19:53:27 +00:00
|
|
|
XModifierKeymap *modmap = XGetModifierMapping ( display );
|
|
|
|
KeyCode kc = XKeysymToKeycode ( display, XK_Num_Lock );
|
|
|
|
KeyCode kc_altl = XKeysymToKeycode ( display, XK_Alt_L );
|
2015-11-20 21:00:37 +00:00
|
|
|
KeyCode kc_altr = XKeysymToKeycode ( display, XK_Alt_R );
|
2015-11-20 19:53:27 +00:00
|
|
|
KeyCode kc_superr = XKeysymToKeycode ( display, XK_Super_R );
|
|
|
|
KeyCode kc_superl = XKeysymToKeycode ( display, XK_Super_L );
|
2015-11-20 21:00:37 +00:00
|
|
|
KeyCode kc_hyperl = XKeysymToKeycode ( display, XK_Hyper_L );
|
|
|
|
KeyCode kc_hyperr = XKeysymToKeycode ( display, XK_Hyper_R );
|
|
|
|
KeyCode kc_metal = XKeysymToKeycode ( display, XK_Meta_L );
|
|
|
|
KeyCode kc_metar = XKeysymToKeycode ( display, XK_Meta_R );
|
2015-02-09 18:35:51 +00:00
|
|
|
for ( int i = 0; i < 8; i++ ) {
|
|
|
|
for ( int j = 0; j < ( int ) modmap->max_keypermod; j++ ) {
|
2015-11-20 21:00:37 +00:00
|
|
|
if ( kc && modmap->modifiermap[i * modmap->max_keypermod + j] == kc ) {
|
2015-02-09 18:35:51 +00:00
|
|
|
NumlockMask = ( 1 << i );
|
|
|
|
}
|
2015-11-20 21:00:37 +00:00
|
|
|
if ( kc_altl && modmap->modifiermap[i * modmap->max_keypermod + j] == kc_altl ) {
|
2015-11-20 19:53:27 +00:00
|
|
|
AltMask |= ( 1 << i );
|
|
|
|
}
|
2015-11-20 21:00:37 +00:00
|
|
|
if ( kc_altr && modmap->modifiermap[i * modmap->max_keypermod + j] == kc_altr ) {
|
|
|
|
AltRMask |= ( 1 << i );
|
|
|
|
}
|
|
|
|
if ( kc_superr && modmap->modifiermap[i * modmap->max_keypermod + j] == kc_superr ) {
|
2015-11-20 19:53:27 +00:00
|
|
|
SuperRMask |= ( 1 << i );
|
|
|
|
}
|
2015-11-20 21:00:37 +00:00
|
|
|
if ( kc_superl && modmap->modifiermap[i * modmap->max_keypermod + j] == kc_superl ) {
|
2015-11-20 19:53:27 +00:00
|
|
|
SuperLMask |= ( 1 << i );
|
2015-11-20 07:40:24 +00:00
|
|
|
}
|
2015-11-20 21:00:37 +00:00
|
|
|
if ( kc_hyperr && modmap->modifiermap[i * modmap->max_keypermod + j] == kc_hyperr ) {
|
|
|
|
HyperRMask |= ( 1 << i );
|
|
|
|
}
|
|
|
|
if ( kc_hyperl && modmap->modifiermap[i * modmap->max_keypermod + j] == kc_hyperl ) {
|
|
|
|
HyperLMask |= ( 1 << i );
|
|
|
|
}
|
|
|
|
if ( kc_metar && modmap->modifiermap[i * modmap->max_keypermod + j] == kc_metar ) {
|
|
|
|
MetaRMask |= ( 1 << i );
|
|
|
|
}
|
|
|
|
if ( kc_metal && modmap->modifiermap[i * modmap->max_keypermod + j] == kc_metal ) {
|
|
|
|
MetaLMask |= ( 1 << i );
|
|
|
|
}
|
2015-02-09 18:35:51 +00:00
|
|
|
}
|
|
|
|
}
|
2015-11-20 21:00:37 +00:00
|
|
|
// Combined mask, without NumLock
|
|
|
|
CombinedMask = ShiftMask | MetaLMask | MetaRMask | AltMask | AltRMask | SuperRMask | SuperLMask | HyperLMask | HyperRMask |
|
|
|
|
ControlMask;
|
2015-02-09 18:35:51 +00:00
|
|
|
XFreeModifiermap ( modmap );
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert a Mod+key arg to mod mask and keysym
|
|
|
|
void x11_parse_key ( char *combo, unsigned int *mod, KeySym *key )
|
|
|
|
{
|
2015-11-20 21:22:11 +00:00
|
|
|
GString *str = g_string_new ( "" );
|
2015-02-09 18:35:51 +00:00
|
|
|
unsigned int modmask = 0;
|
|
|
|
|
|
|
|
if ( strcasestr ( combo, "shift" ) ) {
|
|
|
|
modmask |= ShiftMask;
|
|
|
|
}
|
|
|
|
if ( strcasestr ( combo, "control" ) ) {
|
|
|
|
modmask |= ControlMask;
|
|
|
|
}
|
|
|
|
if ( strcasestr ( combo, "alt" ) ) {
|
2015-11-20 19:53:27 +00:00
|
|
|
modmask |= AltMask;
|
2015-11-20 21:00:37 +00:00
|
|
|
if ( AltMask == 0 ) {
|
2015-11-20 21:22:11 +00:00
|
|
|
g_string_append_printf ( str, "X11 configured keyboard has no <b>Alt</b> key.\n" );
|
2015-11-20 21:00:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( strcasestr ( combo, "altgr" ) ) {
|
|
|
|
modmask |= AltRMask;
|
|
|
|
if ( AltRMask == 0 ) {
|
2015-11-20 21:22:11 +00:00
|
|
|
g_string_append_printf ( str, "X11 configured keyboard has no <b>AltGR</b> key.\n" );
|
2015-11-20 21:00:37 +00:00
|
|
|
}
|
2015-11-20 19:53:27 +00:00
|
|
|
}
|
|
|
|
if ( strcasestr ( combo, "superr" ) ) {
|
|
|
|
modmask |= SuperRMask;
|
2015-11-20 21:00:37 +00:00
|
|
|
if ( SuperRMask == 0 ) {
|
2015-11-20 21:22:11 +00:00
|
|
|
g_string_append_printf ( str, "X11 configured keyboard has no <b>SuperR</b> key.\n" );
|
2015-11-20 21:00:37 +00:00
|
|
|
}
|
2015-11-20 19:53:27 +00:00
|
|
|
}
|
|
|
|
if ( strcasestr ( combo, "superl" ) ) {
|
|
|
|
modmask |= SuperLMask;
|
2015-11-20 21:00:37 +00:00
|
|
|
if ( SuperLMask == 0 ) {
|
2015-11-20 21:22:11 +00:00
|
|
|
g_string_append_printf ( str, "X11 configured keyboard has no <b>SuperL</b> key.\n" );
|
2015-11-20 21:00:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( strcasestr ( combo, "metal" ) ) {
|
|
|
|
modmask |= MetaLMask;
|
|
|
|
if ( MetaLMask == 0 ) {
|
2015-11-20 21:22:11 +00:00
|
|
|
g_string_append_printf ( str, "X11 configured keyboard has no <b>MetaL</b> key.\n" );
|
2015-11-20 21:00:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( strcasestr ( combo, "metar" ) ) {
|
|
|
|
modmask |= MetaRMask;
|
|
|
|
if ( MetaRMask == 0 ) {
|
2015-11-20 21:22:11 +00:00
|
|
|
g_string_append_printf ( str, "X11 configured keyboard has no <b>MetaR</b> key.\n" );
|
2015-11-20 21:00:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( strcasestr ( combo, "hyperl" ) ) {
|
|
|
|
modmask |= HyperLMask;
|
|
|
|
if ( HyperLMask == 0 ) {
|
2015-11-20 21:22:11 +00:00
|
|
|
g_string_append_printf ( str, "X11 configured keyboard has no <b>HyperL</b> key.\n" );
|
2015-11-20 21:00:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( strcasestr ( combo, "hyperr" ) ) {
|
|
|
|
modmask |= HyperRMask;
|
|
|
|
if ( HyperRMask == 0 ) {
|
2015-11-20 21:22:11 +00:00
|
|
|
g_string_append_printf ( str, "X11 configured keyboard has no <b>HyperR</b> key.\n" );
|
2015-11-20 21:00:37 +00:00
|
|
|
}
|
|
|
|
}
|
2015-12-04 07:54:27 +00:00
|
|
|
int seen_mod = FALSE;
|
2015-12-04 08:50:53 +00:00
|
|
|
if ( strcasestr ( combo, "Mod" ) ) {
|
2015-12-04 07:54:27 +00:00
|
|
|
seen_mod = TRUE;
|
|
|
|
}
|
2015-02-09 18:35:51 +00:00
|
|
|
|
2015-02-16 01:03:26 +00:00
|
|
|
*mod = modmask;
|
2015-02-09 18:35:51 +00:00
|
|
|
|
|
|
|
// Skip modifier (if exist) and parse key.
|
|
|
|
char i = strlen ( combo );
|
|
|
|
|
|
|
|
while ( i > 0 && !strchr ( "-+", combo[i - 1] ) ) {
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
|
|
|
|
KeySym sym = XStringToKeysym ( combo + i );
|
|
|
|
|
|
|
|
if ( sym == NoSymbol || ( !modmask && ( strchr ( combo, '-' ) || strchr ( combo, '+' ) ) ) ) {
|
2015-07-29 07:37:40 +00:00
|
|
|
// TODO popup
|
2015-11-20 21:22:11 +00:00
|
|
|
g_string_append_printf ( str, "Sorry, rofi cannot understand the key combination: <i>%s</i>\n", combo );
|
2015-12-04 08:50:53 +00:00
|
|
|
g_string_append ( str, "\nRofi supports the following modifiers:\n\t" );
|
|
|
|
g_string_append ( str, "<i>Shift,Control,Alt,AltGR,SuperL,SuperR," );
|
|
|
|
g_string_append ( str, "MetaL,MetaR,HyperL,HyperR</i>" );
|
|
|
|
if ( seen_mod ) {
|
|
|
|
g_string_append ( str, "\n\n<b>Mod1,Mod2,Mod3,Mod4,Mod5 are no longer supported, use one of the above.</b>" );
|
2015-12-04 07:54:27 +00:00
|
|
|
}
|
2015-02-09 18:35:51 +00:00
|
|
|
}
|
2015-11-20 21:22:11 +00:00
|
|
|
if ( str->len > 0 ) {
|
|
|
|
show_error_message ( str->str, TRUE );
|
|
|
|
g_string_free ( str, TRUE );
|
|
|
|
exit ( EXIT_FAILURE );
|
|
|
|
}
|
|
|
|
g_string_free ( str, TRUE );
|
2015-02-09 18:35:51 +00:00
|
|
|
*key = sym;
|
|
|
|
}
|
|
|
|
|
|
|
|
void x11_set_window_opacity ( Display *display, Window box, unsigned int opacity )
|
|
|
|
{
|
2015-02-10 07:12:03 +00:00
|
|
|
// Scale 0-100 to 0 - UINT32_MAX.
|
2015-02-09 18:35:51 +00:00
|
|
|
unsigned int opacity_set = ( unsigned int ) ( ( opacity / 100.0 ) * UINT32_MAX );
|
2015-02-10 07:12:03 +00:00
|
|
|
// Set opacity.
|
2015-09-19 10:57:48 +00:00
|
|
|
XChangeProperty ( display, box, netatoms[_NET_WM_WINDOW_OPACITY], XA_CARDINAL, 32, PropModeReplace,
|
2015-02-09 18:35:51 +00:00
|
|
|
( unsigned char * ) &opacity_set, 1L );
|
|
|
|
}
|
|
|
|
|
2015-02-10 07:12:03 +00:00
|
|
|
/**
|
|
|
|
* @param display The connection to the X server.
|
|
|
|
*
|
|
|
|
* Fill in the list of Atoms.
|
|
|
|
*/
|
2015-02-09 18:35:51 +00:00
|
|
|
static void x11_create_frequently_used_atoms ( Display *display )
|
|
|
|
{
|
|
|
|
// X atom values
|
|
|
|
for ( int i = 0; i < NUM_NETATOMS; i++ ) {
|
|
|
|
netatoms[i] = XInternAtom ( display, netatom_names[i], False );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ( *xerror )( Display *, XErrorEvent * );
|
|
|
|
/**
|
2015-02-10 07:12:03 +00:00
|
|
|
* @param d The connection to the X server.
|
2015-02-09 18:35:51 +00:00
|
|
|
* @param ee The XErrorEvent
|
|
|
|
*
|
|
|
|
* X11 Error handler.
|
|
|
|
*/
|
|
|
|
static int display_oops ( Display *d, XErrorEvent *ee )
|
|
|
|
{
|
2015-09-19 18:59:50 +00:00
|
|
|
if ( ee->error_code == BadWindow || ( ee->request_code == X_GrabButton && ee->error_code == BadAccess )
|
|
|
|
|| ( ee->request_code == X_GrabKey && ee->error_code == BadAccess ) ) {
|
2015-02-09 18:35:51 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf ( stderr, "error: request code=%d, error code=%d\n", ee->request_code, ee->error_code );
|
|
|
|
return xerror ( d, ee );
|
|
|
|
}
|
|
|
|
|
|
|
|
void x11_setup ( Display *display )
|
|
|
|
{
|
|
|
|
// Set error handle
|
|
|
|
XSync ( display, False );
|
|
|
|
xerror = XSetErrorHandler ( display_oops );
|
|
|
|
XSync ( display, False );
|
|
|
|
|
|
|
|
// determine numlock mask so we can bind on keys with and without it
|
|
|
|
x11_figure_out_numlock_mask ( display );
|
|
|
|
x11_create_frequently_used_atoms ( display );
|
|
|
|
}
|
2015-02-14 18:42:04 +00:00
|
|
|
|
|
|
|
extern XVisualInfo vinfo;
|
2015-02-15 20:15:16 +00:00
|
|
|
int truecolor = FALSE;
|
2015-02-14 18:42:04 +00:00
|
|
|
void create_visual_and_colormap ( Display *display )
|
|
|
|
{
|
|
|
|
int screen = DefaultScreen ( display );
|
|
|
|
// Try to create TrueColor map
|
|
|
|
if ( XMatchVisualInfo ( display, screen, 32, TrueColor, &vinfo ) ) {
|
|
|
|
// Visual found, lets try to create map.
|
2015-09-19 18:59:50 +00:00
|
|
|
map = XCreateColormap ( display, DefaultRootWindow ( display ), vinfo.visual, AllocNone );
|
2015-02-14 18:42:04 +00:00
|
|
|
truecolor = TRUE;
|
|
|
|
}
|
|
|
|
// Failed to create map.
|
|
|
|
// Use the defaults then.
|
|
|
|
if ( map == None ) {
|
|
|
|
truecolor = FALSE;
|
|
|
|
// Two fields we use.
|
|
|
|
vinfo.visual = DefaultVisual ( display, screen );
|
|
|
|
vinfo.depth = DefaultDepth ( display, screen );
|
|
|
|
map = DefaultColormap ( display, screen );
|
|
|
|
}
|
|
|
|
}
|
2015-09-27 09:46:19 +00:00
|
|
|
|
2015-09-27 10:57:54 +00:00
|
|
|
cairo_format_t get_format ( void )
|
2015-09-27 09:46:19 +00:00
|
|
|
{
|
2015-09-27 10:57:54 +00:00
|
|
|
if ( truecolor ) {
|
2015-09-27 09:46:19 +00:00
|
|
|
return CAIRO_FORMAT_ARGB32;
|
|
|
|
}
|
|
|
|
return CAIRO_FORMAT_RGB24;
|
|
|
|
}
|
|
|
|
|
2015-07-31 10:23:41 +00:00
|
|
|
unsigned int color_get ( Display *display, const char *const name, const char * const defn )
|
2015-02-14 18:42:04 +00:00
|
|
|
{
|
2015-10-10 11:43:28 +00:00
|
|
|
char *copy = g_strdup ( name );
|
|
|
|
char *cname = g_strstrip ( copy );
|
|
|
|
XColor color = { 0, 0, 0, 0, 0, 0 };
|
2015-07-31 10:23:41 +00:00
|
|
|
XColor def;
|
2015-02-14 18:42:04 +00:00
|
|
|
// Special format.
|
2015-10-10 11:43:28 +00:00
|
|
|
if ( strncmp ( cname, "argb:", 5 ) == 0 ) {
|
|
|
|
color.pixel = strtoul ( &cname[5], NULL, 16 );
|
2015-09-28 19:41:58 +00:00
|
|
|
color.red = ( ( color.pixel & 0x00FF0000 ) >> 16 ) * 256;
|
|
|
|
color.green = ( ( color.pixel & 0x0000FF00 ) >> 8 ) * 256;
|
|
|
|
color.blue = ( ( color.pixel & 0x000000FF ) ) * 256;
|
2015-02-14 18:42:04 +00:00
|
|
|
if ( !truecolor ) {
|
|
|
|
// This will drop alpha part.
|
2015-07-07 19:50:09 +00:00
|
|
|
Status st = XAllocColor ( display, map, &color );
|
|
|
|
if ( st == None ) {
|
2015-10-10 11:43:28 +00:00
|
|
|
fprintf ( stderr, "Failed to parse color: '%s'\n", cname );
|
2015-07-31 10:23:41 +00:00
|
|
|
st = XAllocNamedColor ( display, map, defn, &color, &def );
|
|
|
|
if ( st == None ) {
|
|
|
|
fprintf ( stderr, "Failed to allocate fallback color\n" );
|
|
|
|
exit ( EXIT_FAILURE );
|
|
|
|
}
|
2015-07-07 19:50:09 +00:00
|
|
|
}
|
2015-02-14 18:42:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2015-10-10 11:43:28 +00:00
|
|
|
Status st = XAllocNamedColor ( display, map, cname, &color, &def );
|
2015-07-07 19:50:09 +00:00
|
|
|
if ( st == None ) {
|
2015-10-10 11:43:28 +00:00
|
|
|
fprintf ( stderr, "Failed to parse color: '%s'\n", cname );
|
2015-07-31 10:23:41 +00:00
|
|
|
st = XAllocNamedColor ( display, map, defn, &color, &def );
|
|
|
|
if ( st == None ) {
|
|
|
|
fprintf ( stderr, "Failed to allocate fallback color\n" );
|
|
|
|
exit ( EXIT_FAILURE );
|
|
|
|
}
|
2015-07-07 19:50:09 +00:00
|
|
|
}
|
2015-02-14 18:42:04 +00:00
|
|
|
}
|
2015-10-10 11:43:28 +00:00
|
|
|
g_free ( copy );
|
|
|
|
return color.pixel;
|
2015-02-14 18:42:04 +00:00
|
|
|
}
|
2015-10-10 11:03:11 +00:00
|
|
|
void x11_helper_set_cairo_rgba ( cairo_t *d, unsigned int pixel )
|
|
|
|
{
|
|
|
|
cairo_set_source_rgba ( d,
|
2015-11-16 20:05:41 +00:00
|
|
|
( ( pixel & 0x00FF0000 ) >> 16 ) / 255.0,
|
|
|
|
( ( pixel & 0x0000FF00 ) >> 8 ) / 255.0,
|
|
|
|
( ( pixel & 0x000000FF ) >> 0 ) / 255.0,
|
|
|
|
( ( pixel & 0xFF000000 ) >> 24 ) / 255.0
|
2015-10-10 11:03:11 +00:00
|
|
|
);
|
|
|
|
}
|
2015-10-10 12:15:27 +00:00
|
|
|
/**
|
|
|
|
* Color cache.
|
|
|
|
*
|
|
|
|
* This stores the current color until
|
|
|
|
*/
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
BACKGROUND,
|
|
|
|
BORDER,
|
|
|
|
SEPARATOR
|
|
|
|
};
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
unsigned int color;
|
|
|
|
unsigned int set;
|
|
|
|
} color_cache[3] = {
|
|
|
|
{ 0, FALSE },
|
|
|
|
{ 0, FALSE },
|
|
|
|
{ 0, FALSE }
|
|
|
|
};
|
|
|
|
void color_cache_reset ( void )
|
|
|
|
{
|
|
|
|
color_cache[BACKGROUND].set = FALSE;
|
|
|
|
color_cache[BORDER].set = FALSE;
|
|
|
|
color_cache[SEPARATOR].set = FALSE;
|
|
|
|
}
|
2015-10-10 11:03:11 +00:00
|
|
|
void color_background ( Display *display, cairo_t *d )
|
2015-04-06 15:13:26 +00:00
|
|
|
{
|
2015-10-10 12:15:27 +00:00
|
|
|
if ( !color_cache[BACKGROUND].set ) {
|
|
|
|
if ( !config.color_enabled ) {
|
|
|
|
color_cache[BACKGROUND].color = color_get ( display, config.menu_bg, "black" );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
gchar **vals = g_strsplit ( config.color_window, ",", 3 );
|
|
|
|
if ( vals != NULL && vals[0] != NULL ) {
|
|
|
|
color_cache[BACKGROUND].color = color_get ( display, vals[0], "black" );
|
|
|
|
}
|
|
|
|
g_strfreev ( vals );
|
2015-04-11 10:04:14 +00:00
|
|
|
}
|
2015-10-10 12:15:27 +00:00
|
|
|
color_cache[BACKGROUND].set = TRUE;
|
2015-04-06 15:13:26 +00:00
|
|
|
}
|
2015-10-10 12:15:27 +00:00
|
|
|
|
|
|
|
x11_helper_set_cairo_rgba ( d, color_cache[BACKGROUND].color );
|
2015-04-06 15:13:26 +00:00
|
|
|
}
|
|
|
|
|
2015-10-10 11:03:11 +00:00
|
|
|
void color_border ( Display *display, cairo_t *d )
|
2015-04-06 15:13:26 +00:00
|
|
|
{
|
2015-10-10 12:15:27 +00:00
|
|
|
if ( !color_cache[BORDER].set ) {
|
|
|
|
if ( !config.color_enabled ) {
|
|
|
|
color_cache[BORDER].color = color_get ( display, config.menu_bc, "white" );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
gchar **vals = g_strsplit ( config.color_window, ",", 3 );
|
|
|
|
if ( vals != NULL && vals[0] != NULL && vals[1] != NULL ) {
|
|
|
|
color_cache[BORDER].color = color_get ( display, vals[1], "white" );
|
|
|
|
}
|
|
|
|
g_strfreev ( vals );
|
2015-04-11 10:04:14 +00:00
|
|
|
}
|
2015-10-10 12:15:27 +00:00
|
|
|
color_cache[BORDER].set = TRUE;
|
2015-04-06 15:13:26 +00:00
|
|
|
}
|
2015-10-10 12:15:27 +00:00
|
|
|
x11_helper_set_cairo_rgba ( d, color_cache[BORDER].color );
|
2015-04-06 15:13:26 +00:00
|
|
|
}
|
2015-08-26 16:11:53 +00:00
|
|
|
|
2015-10-10 11:03:11 +00:00
|
|
|
void color_separator ( Display *display, cairo_t *d )
|
2015-08-26 16:11:53 +00:00
|
|
|
{
|
2015-10-10 12:15:27 +00:00
|
|
|
if ( !color_cache[SEPARATOR].set ) {
|
|
|
|
if ( !config.color_enabled ) {
|
|
|
|
color_cache[SEPARATOR].color = color_get ( display, config.menu_bc, "white" );
|
2015-08-26 16:11:53 +00:00
|
|
|
}
|
2015-10-10 12:15:27 +00:00
|
|
|
else {
|
|
|
|
gchar **vals = g_strsplit ( config.color_window, ",", 3 );
|
|
|
|
if ( vals != NULL && vals[0] != NULL && vals[1] != NULL && vals[2] != NULL ) {
|
|
|
|
color_cache[SEPARATOR].color = color_get ( display, vals[2], "white" );
|
|
|
|
}
|
|
|
|
else if ( vals != NULL && vals[0] != NULL && vals[1] != NULL ) {
|
|
|
|
color_cache[SEPARATOR].color = color_get ( display, vals[1], "white" );
|
|
|
|
}
|
|
|
|
g_strfreev ( vals );
|
2015-08-26 16:11:53 +00:00
|
|
|
}
|
2015-10-10 12:15:27 +00:00
|
|
|
color_cache[SEPARATOR].set = TRUE;
|
2015-08-26 16:11:53 +00:00
|
|
|
}
|
2015-10-10 12:15:27 +00:00
|
|
|
x11_helper_set_cairo_rgba ( d, color_cache[SEPARATOR].color );
|
2015-08-26 16:11:53 +00:00
|
|
|
}
|