~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kwin/utils.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/********************************************************************
 
2
 KWin - the KDE window manager
 
3
 This file is part of the KDE project.
 
4
 
 
5
Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
 
6
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
 
7
 
 
8
This program is free software; you can redistribute it and/or modify
 
9
it under the terms of the GNU General Public License as published by
 
10
the Free Software Foundation; either version 2 of the License, or
 
11
(at your option) any later version.
 
12
 
 
13
This program is distributed in the hope that it will be useful,
 
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
GNU General Public License for more details.
 
17
 
 
18
You should have received a copy of the GNU General Public License
 
19
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*********************************************************************/
 
21
 
 
22
/*
 
23
 
 
24
 This file is for (very) small utility functions/classes.
 
25
 
 
26
*/
 
27
 
 
28
#include "utils.h"
 
29
 
 
30
#include <unistd.h>
 
31
 
 
32
#ifndef KCMRULES
 
33
#include <QLabel>
 
34
#include <QVBoxLayout>
 
35
#include <kxerrorhandler.h>
 
36
#include <assert.h>
 
37
#include <kdebug.h>
 
38
#include <kglobalaccel.h>
 
39
#include <klocale.h>
 
40
#include <kshortcut.h>
 
41
#include <kkeyserver.h>
 
42
#include <KPushButton>
 
43
 
 
44
#include <X11/Xlib.h>
 
45
#include <X11/extensions/shape.h>
 
46
#include <X11/Xatom.h>
 
47
#include <QX11Info>
 
48
#include <QtGui/QKeySequence>
 
49
 
 
50
#include <stdio.h>
 
51
 
 
52
#include "atoms.h"
 
53
#include "notifications.h"
 
54
#include "workspace.h"
 
55
 
 
56
#endif
 
57
 
 
58
namespace KWin
 
59
{
 
60
 
 
61
#ifndef KCMRULES
 
62
 
 
63
//************************************
 
64
// StrutRect
 
65
//************************************
 
66
 
 
67
StrutRect::StrutRect(QRect rect, StrutArea area)
 
68
    : QRect(rect)
 
69
    , m_area(area)
 
70
{
 
71
}
 
72
 
 
73
StrutRect::StrutRect(const StrutRect& other)
 
74
    : QRect(other)
 
75
    , m_area(other.area())
 
76
{
 
77
}
 
78
 
 
79
//************************************
 
80
// Motif
 
81
//************************************
 
82
 
 
83
void Motif::readFlags(WId w, bool& got_noborder, bool& noborder,
 
84
                      bool& resize, bool& move, bool& minimize, bool& maximize, bool& close)
 
85
{
 
86
    Atom type;
 
87
    int format;
 
88
    unsigned long length, after;
 
89
    unsigned char* data;
 
90
    MwmHints* hints = 0;
 
91
    if (XGetWindowProperty(display(), w, atoms->motif_wm_hints, 0, 5,
 
92
                          false, atoms->motif_wm_hints, &type, &format,
 
93
                          &length, &after, &data) == Success) {
 
94
        if (data)
 
95
            hints = (MwmHints*) data;
 
96
    }
 
97
    got_noborder = false;
 
98
    noborder = false;
 
99
    resize = true;
 
100
    move = true;
 
101
    minimize = true;
 
102
    maximize = true;
 
103
    close = true;
 
104
    if (hints) {
 
105
        // To quote from Metacity 'We support those MWM hints deemed non-stupid'
 
106
        if (hints->flags & MWM_HINTS_FUNCTIONS) {
 
107
            // if MWM_FUNC_ALL is set, other flags say what to turn _off_
 
108
            bool set_value = ((hints->functions & MWM_FUNC_ALL) == 0);
 
109
            resize = move = minimize = maximize = close = !set_value;
 
110
            if (hints->functions & MWM_FUNC_RESIZE)
 
111
                resize = set_value;
 
112
            if (hints->functions & MWM_FUNC_MOVE)
 
113
                move = set_value;
 
114
            if (hints->functions & MWM_FUNC_MINIMIZE)
 
115
                minimize = set_value;
 
116
            if (hints->functions & MWM_FUNC_MAXIMIZE)
 
117
                maximize = set_value;
 
118
            if (hints->functions & MWM_FUNC_CLOSE)
 
119
                close = set_value;
 
120
        }
 
121
        if (hints->flags & MWM_HINTS_DECORATIONS) {
 
122
            got_noborder = true;
 
123
            noborder = !hints->decorations;
 
124
        }
 
125
        XFree(data);
 
126
    }
 
127
}
 
128
 
 
129
//************************************
 
130
// KWinSelectionOwner
 
131
//************************************
 
132
 
 
133
KWinSelectionOwner::KWinSelectionOwner(int screen_P)
 
134
    : KSelectionOwner(make_selection_atom(screen_P), screen_P)
 
135
{
 
136
}
 
137
 
 
138
Atom KWinSelectionOwner::make_selection_atom(int screen_P)
 
139
{
 
140
    if (screen_P < 0)
 
141
        screen_P = DefaultScreen(display());
 
142
    char tmp[ 30 ];
 
143
    sprintf(tmp, "WM_S%d", screen_P);
 
144
    return XInternAtom(display(), tmp, False);
 
145
}
 
146
 
 
147
void KWinSelectionOwner::getAtoms()
 
148
{
 
149
    KSelectionOwner::getAtoms();
 
150
    if (xa_version == None) {
 
151
        Atom atoms[ 1 ];
 
152
        const char* const names[] =
 
153
        { "VERSION" };
 
154
        XInternAtoms(display(), const_cast< char** >(names), 1, False, atoms);
 
155
        xa_version = atoms[ 0 ];
 
156
    }
 
157
}
 
158
 
 
159
void KWinSelectionOwner::replyTargets(Atom property_P, Window requestor_P)
 
160
{
 
161
    KSelectionOwner::replyTargets(property_P, requestor_P);
 
162
    Atom atoms[ 1 ] = { xa_version };
 
163
    // PropModeAppend !
 
164
    XChangeProperty(display(), requestor_P, property_P, XA_ATOM, 32, PropModeAppend,
 
165
    reinterpret_cast< unsigned char* >(atoms), 1);
 
166
}
 
167
 
 
168
bool KWinSelectionOwner::genericReply(Atom target_P, Atom property_P, Window requestor_P)
 
169
{
 
170
    if (target_P == xa_version) {
 
171
        long version[] = { 2, 0 };
 
172
        XChangeProperty(display(), requestor_P, property_P, XA_INTEGER, 32,
 
173
        PropModeReplace, reinterpret_cast< unsigned char* >(&version), 2);
 
174
    } else
 
175
        return KSelectionOwner::genericReply(target_P, property_P, requestor_P);
 
176
    return true;
 
177
}
 
178
 
 
179
Atom KWinSelectionOwner::xa_version = None;
 
180
 
 
181
 
 
182
QByteArray getStringProperty(WId w, Atom prop, char separator)
 
183
{
 
184
    Atom type;
 
185
    int format, status;
 
186
    unsigned long nitems = 0;
 
187
    unsigned long extra = 0;
 
188
    unsigned char *data = 0;
 
189
    QByteArray result = "";
 
190
    KXErrorHandler handler; // ignore errors
 
191
    status = XGetWindowProperty(display(), w, prop, 0, 10000,
 
192
    false, XA_STRING, &type, &format,
 
193
    &nitems, &extra, &data);
 
194
    if (status == Success) {
 
195
        if (data && separator) {
 
196
            for (int i = 0; i < (int)nitems; i++)
 
197
                if (!data[i] && i + 1 < (int)nitems)
 
198
                    data[i] = separator;
 
199
        }
 
200
        if (data)
 
201
            result = (const char*) data;
 
202
        XFree(data);
 
203
    }
 
204
    return result;
 
205
}
 
206
 
 
207
static Time next_x_time;
 
208
static Bool update_x_time_predicate(Display*, XEvent* event, XPointer)
 
209
{
 
210
    if (next_x_time != CurrentTime)
 
211
        return False;
 
212
    // from qapplication_x11.cpp
 
213
    switch(event->type) {
 
214
    case ButtonPress:
 
215
        // fallthrough intended
 
216
    case ButtonRelease:
 
217
        next_x_time = event->xbutton.time;
 
218
        break;
 
219
    case MotionNotify:
 
220
        next_x_time = event->xmotion.time;
 
221
        break;
 
222
    case KeyPress:
 
223
        // fallthrough intended
 
224
    case KeyRelease:
 
225
        next_x_time = event->xkey.time;
 
226
        break;
 
227
    case PropertyNotify:
 
228
        next_x_time = event->xproperty.time;
 
229
        break;
 
230
    case EnterNotify:
 
231
    case LeaveNotify:
 
232
        next_x_time = event->xcrossing.time;
 
233
        break;
 
234
    case SelectionClear:
 
235
        next_x_time = event->xselectionclear.time;
 
236
        break;
 
237
    default:
 
238
        break;
 
239
    }
 
240
    return False;
 
241
}
 
242
 
 
243
/*
 
244
 Updates xTime(). This used to simply fetch current timestamp from the server,
 
245
 but that can cause xTime() to be newer than timestamp of events that are
 
246
 still in our events queue, thus e.g. making XSetInputFocus() caused by such
 
247
 event to be ignored. Therefore events queue is searched for first
 
248
 event with timestamp, and extra PropertyNotify is generated in order to make
 
249
 sure such event is found.
 
250
*/
 
251
void updateXTime()
 
252
{
 
253
    static QWidget* w = 0;
 
254
    if (!w)
 
255
        w = new QWidget;
 
256
    long data = 1;
 
257
    XChangeProperty(display(), w->winId(), atoms->kwin_running, atoms->kwin_running, 32,
 
258
    PropModeAppend, (unsigned char*) &data, 1);
 
259
    next_x_time = CurrentTime;
 
260
    XEvent dummy;
 
261
    XCheckIfEvent(display(), &dummy, update_x_time_predicate, NULL);
 
262
    if (next_x_time == CurrentTime) {
 
263
        XSync(display(), False);
 
264
        XCheckIfEvent(display(), &dummy, update_x_time_predicate, NULL);
 
265
    }
 
266
    assert(next_x_time != CurrentTime);
 
267
    QX11Info::setAppTime(next_x_time);
 
268
    XEvent ev; // remove the PropertyNotify event from the events queue
 
269
    XWindowEvent(display(), w->winId(), PropertyChangeMask, &ev);
 
270
}
 
271
 
 
272
static int server_grab_count = 0;
 
273
 
 
274
void grabXServer()
 
275
{
 
276
    if (++server_grab_count == 1)
 
277
        XGrabServer(display());
 
278
}
 
279
 
 
280
void ungrabXServer()
 
281
{
 
282
    assert(server_grab_count > 0);
 
283
    if (--server_grab_count == 0) {
 
284
        XUngrabServer(display());
 
285
        XFlush(display());
 
286
        Notify::sendPendingEvents();
 
287
    }
 
288
}
 
289
 
 
290
bool grabbedXServer()
 
291
{
 
292
    return server_grab_count > 0;
 
293
}
 
294
 
 
295
static bool keyboard_grabbed = false;
 
296
 
 
297
bool grabXKeyboard(Window w)
 
298
{
 
299
    if (QWidget::keyboardGrabber() != NULL)
 
300
        return false;
 
301
    if (keyboard_grabbed)
 
302
        return false;
 
303
    if (qApp->activePopupWidget() != NULL)
 
304
        return false;
 
305
    if (w == None)
 
306
        w = rootWindow();
 
307
    if (XGrabKeyboard(display(), w, False,
 
308
    GrabModeAsync, GrabModeAsync, xTime()) != GrabSuccess)
 
309
        return false;
 
310
    keyboard_grabbed = true;
 
311
    return true;
 
312
}
 
313
 
 
314
void ungrabXKeyboard()
 
315
{
 
316
    if (!keyboard_grabbed) {
 
317
        // grabXKeyboard() may fail sometimes, so don't fail, but at least warn anyway
 
318
        kDebug(1212) << "ungrabXKeyboard() called but keyboard not grabbed!";
 
319
    }
 
320
    keyboard_grabbed = false;
 
321
    XUngrabKeyboard(display(), CurrentTime);
 
322
}
 
323
 
 
324
QPoint cursorPos()
 
325
{
 
326
    return Workspace::self()->cursorPos();
 
327
}
 
328
 
 
329
// converting between X11 mouse/keyboard state mask and Qt button/keyboard states
 
330
 
 
331
int qtToX11Button(Qt::MouseButton button)
 
332
{
 
333
    if (button == Qt::LeftButton)
 
334
        return Button1;
 
335
    else if (button == Qt::MidButton)
 
336
        return Button2;
 
337
    else if (button == Qt::RightButton)
 
338
        return Button3;
 
339
    return AnyButton; // 0
 
340
}
 
341
 
 
342
Qt::MouseButton x11ToQtMouseButton(int button)
 
343
{
 
344
    if (button == Button1)
 
345
        return Qt::LeftButton;
 
346
    if (button == Button2)
 
347
        return Qt::MidButton;
 
348
    if (button == Button3)
 
349
        return Qt::RightButton;
 
350
    if (button == Button4)
 
351
        return Qt::XButton1;
 
352
    if (button == Button5)
 
353
        return Qt::XButton2;
 
354
    return Qt::NoButton;
 
355
}
 
356
 
 
357
int qtToX11State(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
 
358
{
 
359
    int ret = 0;
 
360
    if (buttons & Qt::LeftButton)
 
361
        ret |= Button1Mask;
 
362
    if (buttons & Qt::MidButton)
 
363
        ret |= Button2Mask;
 
364
    if (buttons & Qt::RightButton)
 
365
        ret |= Button3Mask;
 
366
    if (modifiers & Qt::ShiftModifier)
 
367
        ret |= ShiftMask;
 
368
    if (modifiers & Qt::ControlModifier)
 
369
        ret |= ControlMask;
 
370
    if (modifiers & Qt::AltModifier)
 
371
        ret |= KKeyServer::modXAlt();
 
372
    if (modifiers & Qt::MetaModifier)
 
373
        ret |= KKeyServer::modXMeta();
 
374
    return ret;
 
375
}
 
376
 
 
377
Qt::MouseButtons x11ToQtMouseButtons(int state)
 
378
{
 
379
    Qt::MouseButtons ret = 0;
 
380
    if (state & Button1Mask)
 
381
        ret |= Qt::LeftButton;
 
382
    if (state & Button2Mask)
 
383
        ret |= Qt::MidButton;
 
384
    if (state & Button3Mask)
 
385
        ret |= Qt::RightButton;
 
386
    if (state & Button4Mask)
 
387
        ret |= Qt::XButton1;
 
388
    if (state & Button5Mask)
 
389
        ret |= Qt::XButton2;
 
390
    return ret;
 
391
}
 
392
 
 
393
Qt::KeyboardModifiers x11ToQtKeyboardModifiers(int state)
 
394
{
 
395
    Qt::KeyboardModifiers ret = 0;
 
396
    if (state & ShiftMask)
 
397
        ret |= Qt::ShiftModifier;
 
398
    if (state & ControlMask)
 
399
        ret |= Qt::ControlModifier;
 
400
    if (state & KKeyServer::modXAlt())
 
401
        ret |= Qt::AltModifier;
 
402
    if (state & KKeyServer::modXMeta())
 
403
        ret |= Qt::MetaModifier;
 
404
    return ret;
 
405
}
 
406
 
 
407
#endif
 
408
 
 
409
bool isLocalMachine(const QByteArray& host)
 
410
{
 
411
#ifdef HOST_NAME_MAX
 
412
    char hostnamebuf[HOST_NAME_MAX];
 
413
#else
 
414
    char hostnamebuf[256];
 
415
#endif
 
416
    if (gethostname(hostnamebuf, sizeof hostnamebuf) >= 0) {
 
417
        hostnamebuf[sizeof(hostnamebuf)-1] = 0;
 
418
        if (host == hostnamebuf)
 
419
            return true;
 
420
        if (char *dot = strchr(hostnamebuf, '.')) {
 
421
            *dot = '\0';
 
422
            if (host == hostnamebuf)
 
423
                return true;
 
424
        } else { // e.g. LibreOffice likes to give FQDN, even if gethostname() doesn't include domain
 
425
            QByteArray h = hostnamebuf;
 
426
            if( getdomainname( hostnamebuf, sizeof hostnamebuf ) >= 0 ) {
 
427
                hostnamebuf[sizeof(hostnamebuf)-1] = 0;
 
428
                if( host == h + '.' + QByteArray( hostnamebuf ))
 
429
                    return true;
 
430
            }
 
431
        }
 
432
    }
 
433
    return false;
 
434
}
 
435
 
 
436
#ifndef KCMRULES
 
437
ShortcutDialog::ShortcutDialog(const QKeySequence& cut)
 
438
    : _shortcut(cut)
 
439
{
 
440
    QWidget *vBoxContainer = new QWidget(this);
 
441
    vBoxContainer->setLayout(new QVBoxLayout(vBoxContainer));
 
442
    vBoxContainer->layout()->addWidget(widget = new KKeySequenceWidget(vBoxContainer));
 
443
    vBoxContainer->layout()->addWidget(warning = new QLabel(vBoxContainer));
 
444
    warning->hide();
 
445
    widget->setKeySequence(cut);
 
446
 
 
447
    // To not check for conflicting shortcuts. The widget would use a message
 
448
    // box which brings down kwin.
 
449
    widget->setCheckForConflictsAgainst(KKeySequenceWidget::None);
 
450
    // It's a global shortcut so don't allow multikey shortcuts
 
451
    widget->setMultiKeyShortcutsAllowed(false);
 
452
 
 
453
    // Listen to changed shortcuts
 
454
    connect(
 
455
        widget, SIGNAL(keySequenceChanged(const QKeySequence&)),
 
456
        SLOT(keySequenceChanged(const QKeySequence&)));
 
457
 
 
458
    setMainWidget(vBoxContainer);
 
459
    widget->setFocus();
 
460
 
 
461
    // make it a popup, so that it has the grab
 
462
    XSetWindowAttributes attrs;
 
463
    attrs.override_redirect = True;
 
464
    XChangeWindowAttributes(display(), winId(), CWOverrideRedirect, &attrs);
 
465
    setWindowFlags(Qt::Popup);
 
466
}
 
467
 
 
468
void ShortcutDialog::accept()
 
469
{
 
470
    QKeySequence seq = shortcut();
 
471
    if (!seq.isEmpty()) {
 
472
        if (seq[0] == Qt::Key_Escape) {
 
473
            reject();
 
474
            return;
 
475
        }
 
476
        if (seq[0] == Qt::Key_Space
 
477
        || (seq[0] & Qt::KeyboardModifierMask) == 0) {
 
478
            // clear
 
479
            widget->clearKeySequence();
 
480
            KDialog::accept();
 
481
            return;
 
482
        }
 
483
    }
 
484
    KDialog::accept();
 
485
}
 
486
 
 
487
void ShortcutDialog::done(int r)
 
488
{
 
489
    KDialog::done(r);
 
490
    emit dialogDone(r == Accepted);
 
491
}
 
492
 
 
493
void ShortcutDialog::keySequenceChanged(const QKeySequence &seq)
 
494
{
 
495
    activateWindow(); // where is the kbd focus lost? cause of popup state?
 
496
    if (_shortcut == seq)
 
497
        return; // don't try to update the same
 
498
 
 
499
    // Check if the key sequence is used currently
 
500
    QString sc = seq.toString();
 
501
    // NOTICE - seq.toString() & the entries in "conflicting" randomly get invalidated after the next call (if no sc has been set & conflicting isn't empty?!)
 
502
    QList<KGlobalShortcutInfo> conflicting = KGlobalAccel::getGlobalShortcutsByKey(seq);
 
503
    if (!conflicting.isEmpty()) {
 
504
        const KGlobalShortcutInfo &conflict = conflicting.at(0);
 
505
        warning->setText(i18nc("'%1' is a keyboard shortcut like 'ctrl+w'",
 
506
        "<b>%1</b> is already in use", sc));
 
507
        warning->setToolTip(i18nc("keyboard shortcut '%1' is used by action '%2' in application '%3'",
 
508
        "<b>%1</b> is used by %2 in %3", sc, conflict.friendlyName(), conflict.componentFriendlyName()));
 
509
        warning->show();
 
510
        widget->setKeySequence(shortcut());
 
511
    } else if (seq != _shortcut) {
 
512
        warning->hide();
 
513
        if (KPushButton *ok = button(KDialog::Ok))
 
514
            ok->setFocus();
 
515
    }
 
516
 
 
517
    _shortcut = seq;
 
518
}
 
519
 
 
520
QKeySequence ShortcutDialog::shortcut() const
 
521
{
 
522
    return _shortcut;
 
523
}
 
524
 
 
525
#endif //KCMRULES
 
526
} // namespace
 
527
 
 
528
#ifndef KCMRULES
 
529
#include "utils.moc"
 
530
#endif