'mplayer' fix now knows about mplayer2
[jrugr.git] / src / jrugr.cpp
blobdc523330f518a4832c51844bbfcb6ca1401d6b98
1 ////////////////////////////////////////
2 // Copyright : GPL //
3 ////////////////////////////////////////
4 #include "jrugr.h"
7 #define K8_MPLAYER_HACK
10 ////////////////////////////////////////////////////////////////////////////////
11 static Atom
12 atomActWin = None,
13 atomClientList = None,
14 atomCurDesk = None;
17 ////////////////////////////////////////////////////////////////////////////////
18 static bool makeFacehugger (Window w) {
19 //WARNING! this WILL NOT work as expected on our own windows!
20 return XSelectInput(QX11Info::display(), w, /*SubstructureNotifyMask |*/ StructureNotifyMask) == Success;
24 static void dumpWinInfo (const char *msg, Window w) {
25 Window pw;
27 if (w == None) return;
28 pw = X11Tools::windowTransientFor(w);
29 qDebug() << msg << (int)(w) <<
30 "class:" << X11Tools::windowClass(w) <<
31 "name:" << X11Tools::windowName(w) <<
32 "pid:" << X11Tools::windowPID(w) <<
33 "transientfor:" << pw;
34 //dumpWinInfo(" parent (tr)", pw);
38 ////////////////////////////////////////////////////////////////////////////////
39 Jrugr::Jrugr (int &argc, char **argv) :
40 QApplication(argc, argv),
42 mActiveGroup(0),
43 mXkb(0),
44 mTrayIcon(0),
45 mTrayMenu(0),
46 jCfg(0),
47 mDeskJustChanged(false)
49 if (atomActWin == None) atomActWin = XInternAtom(QX11Info::display(), "_NET_ACTIVE_WINDOW", True);
50 if (atomClientList == None) atomClientList = XInternAtom(QX11Info::display(), "_NET_CLIENT_LIST", True);
51 if (atomCurDesk == None) atomCurDesk = XInternAtom(QX11Info::display(), "_NET_CURRENT_DESKTOP", True);
52 // mandatory to keep it running after closing about dialogs...
53 setQuitOnLastWindowClosed(false);
54 setWindowIcon(QIcon(":/about/jrugr.png"));
56 mCfgFileName = QDir::homePath()+"/.config/jrugr.cfg";
60 Jrugr::~Jrugr () {
61 delete mXkb;
62 delete mTrayMenu;
63 delete jCfg;
64 //! \warning the trayIcon *has* to be deleted here to prevent XFreeColormap() free corruption.
65 delete mTrayIcon;
69 bool Jrugr::firstStart () {
70 if (!QFile::exists(mCfgFileName)) {
71 QString themePath;
73 qDebug() << " Jrugr:Config file not found in:" << mCfgFileName;
74 QSettings jrugr(mCfgFileName, QSettings::IniFormat);
75 QDir themeDir("/usr/share/jrugr/theme/default");
76 if (themeDir.exists()) themePath = themeDir.path();
77 else themePath = QCoreApplication::applicationDirPath()+"/theme/default";;
78 jrugr.beginGroup("Style");
79 mLangThemePath = themePath;
80 jrugr.setValue("path", mLangThemePath);
81 jrugr.endGroup(); //Style
82 return true;
84 return false;
88 void Jrugr::startup () {
89 if (firstStart()) configure();
92 QSettings jrugr(mCfgFileName, QSettings::IniFormat, this);
93 jrugr.beginGroup("Style");
94 mLangThemePath = jrugr.value("path").toString()+"/language/";
95 jrugr.endGroup(); //Style
97 jCfg = JrugrCfg::load(mCfgFileName);
98 if (!jCfg) abort();
99 if (jCfg->workMode == USE_XKB) configureXkb();
101 mXkb = new XKeyboard();
102 mXkb->groupInfo(mGroupInfo);
104 connect(mXkb, SIGNAL(groupChanged(int)), this, SLOT(onGroupChanged(int)), Qt::QueuedConnection);
105 connect(mXkb, SIGNAL(layoutChanged()), this, SLOT(onLayoutChanged()), Qt::QueuedConnection);
106 connect(this, SIGNAL(changeLayout(int)), this, SLOT(onChangeLayout(int)), Qt::QueuedConnection);
108 connect(this, SIGNAL(activeWindowChanged()), this, SLOT(doActiveWindowChanged()), Qt::QueuedConnection);
109 connect(this, SIGNAL(activeDesktopChanged()), this, SLOT(doActiveDesktopChanged()), Qt::QueuedConnection);
110 connect(this, SIGNAL(clientListChanged()), this, SLOT(onClientListChanged()), Qt::QueuedConnection);
114 void Jrugr::initialize () {
115 mXkb->groupInfo(mGroupInfo);
117 mActiveGroup = mXkb->activeGroup();
118 mActiveWindow = X11Tools::activeWindow();
119 mCurDesk = X11Tools::activeDesktop();
121 mTrayIcon = new QSystemTrayIcon(this);
122 connect(mTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayClicked(QSystemTrayIcon::ActivationReason)));
124 createMenu();
125 buildIcon();
127 installFacehuggers();
131 void Jrugr::reconfigure () {
132 JrugrCfg *newConf = JrugrCfg::load(mCfgFileName);
133 if (newConf) {
134 QXkbLayoutList nl;
136 mXkb->groupInfo(nl);
137 bool diffGI = nl.size() != mGroupInfo.size();
139 if (!diffGI) {
140 for (int f = nl.size()-1; f >= 0; --f) if (nl[f].name != mGroupInfo[f].name || nl[f].sym != mGroupInfo[f].sym) { diffGI = true; break; }
143 if (diffGI || newConf->layouts != jCfg->layouts || newConf->showFlag != jCfg->showFlag ||
144 newConf->workMode != jCfg->workMode || newConf->switching != jCfg->switching) {
145 delete jCfg;
146 jCfg = newConf;
147 createMenu();
148 buildIcon();
149 } else {
150 delete newConf;
156 int Jrugr::setKeyLayout (QString keyConf) {
157 qDebug() << "Jrugr::setKeyLayout:" << keyConf;
158 QStringList argument = keyConf.split(" ");
159 qDebug() << " Jrugr:setxkbmap argumetns:" << argument;
160 int result = QProcess::execute("setxkbmap", argument);
161 qDebug() << " Jrugr:setxkbmap result:" << result;
162 return result;
166 void Jrugr::configureXkb () {
167 QString layout, variant, option;
168 QString model = jCfg->model;
170 for (int f = 0; f < jCfg->layouts.count(); ++f) {
171 layout += jCfg->layouts[f].layout;
172 variant += jCfg->layouts[f].variant;
173 if (f < jCfg->layouts.count()-1) { layout += ','; variant += ','; }
175 option = jCfg->options.join(",");
176 qDebug() << "Set layout:" << layout;
177 if (!(model.isEmpty() || model.isNull()) && !(layout.isEmpty() || layout.isNull())) {
178 QString args = "-model "+model+" -layout "+layout;
179 QString tmp = variant;
180 if (!variant.isNull() && !variant.isEmpty() && !tmp.remove(",").isEmpty()) args +=" -variant "+variant;
181 if (!option.isNull() && !option.isEmpty()) args +=" -option "+option;
182 qDebug() << " Jrugr:XKB args " << args;
183 if (setKeyLayout(args) == QProcess::CrashExit) {
184 qDebug() << " Jrugr:XKB isn`t set";
185 qDebug() << " Jrugr:------------------------------";
186 return;
188 qDebug() << " Jrugr:XKB set";
193 void Jrugr::configure () {
194 JrugrConfigForm *mCfgForm = new JrugrConfigForm(mCfgFileName);
195 connect(mCfgForm, SIGNAL(saveConfig()), SLOT(reconfigure()));
196 mCfgForm->exec(); // the form will die on closing
200 bool Jrugr::isKnownWindow (Window w) const {
201 return mKnownWindows.contains(w);
205 bool Jrugr::isKnownApp (Window w) const {
206 return mKnownApps.contains(X11Tools::appWindow(w));
210 WinInfo *Jrugr::findWindowInfo (Window w) {
211 return mKnownWindows.value(w, 0);
215 // find application info for the given window
216 WinInfo *Jrugr::findAppInfo (Window w) {
217 return mKnownApps.value(X11Tools::appWindow(w), 0);
221 int Jrugr::desktopLayout (int desk) const {
222 if (desk < 0 || desk >= mDeskLangs.size()) return 0;
223 return mDeskLangs.at(desk);
227 void Jrugr::setDesktopLayout (int desk, int group) {
228 if (desk < 0 || desk > 42) return; // invalid desktop number
229 while (mDeskLangs.size() <= desk) mDeskLangs << 0;
230 mDeskLangs[desk] = group;
234 void Jrugr::installFacehuggers () {
235 QX11WindowList wl = X11Tools::topLevelWindows();
236 QSet<WId> qtwl;
239 //QWidgetList twl = QApplication::allWidgets();
240 QWidgetList twl = QApplication::topLevelWidgets();
241 foreach (QWidget *wd, twl) {
242 WId w = wd->effectiveWinId();
243 if (w != None) qtwl << w;
247 for (int f = 0; f < wl.size(); ++f) {
248 if (wl[f] != None && !qtwl.contains(wl[f]) && !isKnownWindow(wl[f])) {
249 WinInfo *wi = new WinInfo;
251 wi->w = wl[f];
252 wi->appWindow = X11Tools::appWindow(wl[f]);
253 wi->layout = 0;
254 mKnownWindows[wl[f]] = wi;
255 makeFacehugger(wi->w);
257 if (wi->appWindow == wi->w && !isKnownApp(wi->w)) {
258 WinInfo *w2 = new WinInfo;
260 w2->appWindow = w2->w = wi->w;
261 w2->layout = wi->layout;
262 mKnownApps[w2->w] = w2;
269 void Jrugr::doActiveWindowChanged () {
270 //qDebug() << "doActiveWindowChanged";
271 onActiveWindowChanged(X11Tools::activeWindow());
275 void Jrugr::doActiveDesktopChanged () {
276 //qDebug() << "doActiveDesktopChanged";
277 onActiveDesktopChanged(X11Tools::activeDesktop());
281 void Jrugr::onLayoutChanged () {
282 qDebug()<<"Jrugr::onLayoutChanged : reconfig";
283 reconfigure();
284 qDebug()<<"Jrugr::onLayoutChanged : done";
288 QIcon Jrugr::langIcon (const QString &lsym, int wdt, int hgt, bool noFlag) {
289 if (!noFlag) {
290 QString path = mLangThemePath+fixLayoutName(lsym, jCfg->useSU);
291 QString SVGfile = path+".svg";
293 if (QFile::exists(SVGfile)) {
294 QSvgRenderer flagSVG(SVGfile);
295 if (flagSVG.isValid()) {
296 QPixmap pix(wdt, hgt);
297 QPainter painter;
298 painter.begin(&pix);
299 flagSVG.render(&painter, QRectF(0, 0, wdt, hgt));
300 painter.end();
301 return QIcon(pix);
305 QString PNGfile = path+".png";
306 if (QFile::exists(PNGfile)) {
307 return QIcon(PNGfile);
312 qDebug() << ":::" << lsym;
313 QFont font("Helvetica [Cronyx]", 15); //FIXME: should be configurable
314 //QFont font("Tahoma", 15);
315 font.setBold(true);
316 font.setLetterSpacing(QFont::PercentageSpacing, 120);
317 QPixmap pix(lsym.length()*20, 26);
318 QPainter painter;
319 painter.begin(&pix);
320 painter.setFont(font);
321 painter.fillRect(pix.rect(), Qt::darkGray);
322 painter.setPen(Qt::white);
323 painter.drawText(pix.rect(), Qt::AlignVCenter|Qt::AlignCenter, lsym);
324 painter.end();
325 return QIcon(pix);
330 void Jrugr::createMenu () {
331 if (!mTrayMenu) {
332 mTrayMenu = new QMenu();
333 connect(mTrayMenu, SIGNAL(triggered(QAction *)), SLOT(actionsActivate(QAction *)));
334 } else {
335 mTrayMenu->clear();
338 for (int index = 0; index < mGroupInfo.size(); ++index) {
339 QString lname = mGroupInfo[index].name, lsym = mGroupInfo[index].sym;
341 QAction *act = new QAction(lname, this);
342 act->setIcon(langIcon(lsym));
343 act->setData(lname);
344 mTrayMenu->addAction(act);
347 mTrayMenu->addSeparator();
348 QAction *config = new QAction(tr("Configure"), this);
349 config->setData("configure");
350 mTrayMenu->addAction(config);
352 mTrayMenu->addSeparator();
353 QAction *about_jrugr = new QAction(tr("About Jrugr"), this);
354 about_jrugr->setData("about_jrugr");
355 mTrayMenu->addAction(about_jrugr);
357 QAction *about_qt = new QAction(tr("About Qt"), this);
358 about_qt->setData("about_qt");
359 mTrayMenu->addAction(about_qt);
361 mTrayMenu->addSeparator();
362 QAction *jrugrExit = new QAction(tr("Exit"), this);
363 jrugrExit->setData("exit");
364 mTrayMenu->addAction(jrugrExit);
368 void Jrugr::buildIcon () {
369 if (!mTrayIcon) return;
370 mTrayIcon->setIcon(langIcon(mGroupInfo[mActiveGroup].sym, 32, 22, !jCfg->showFlag));
371 mTrayIcon->setToolTip(mGroupInfo[mActiveGroup].name);
372 if (mTrayMenu) mTrayIcon->setContextMenu(mTrayMenu);
373 mTrayIcon->show();
377 void Jrugr::trayClicked (QSystemTrayIcon::ActivationReason reason) {
378 switch (reason) {
379 case QSystemTrayIcon::Trigger: setNextGroup(); break;
380 case QSystemTrayIcon::DoubleClick: setNextGroup(); break;
381 case QSystemTrayIcon::MiddleClick: setPrevGroup(); break;
382 default: ;
387 // new layout activated; fix all necessary shit
388 void Jrugr::onGroupChanged (int index) {
389 qDebug() << "Jrugr::onGroupChanged: index:" << index;
390 mActiveGroup = index;
391 buildIcon();
393 if (mActiveWindow != None) {
394 WinInfo *wi = findWindowInfo(mActiveWindow);
396 if (!wi) {
397 qDebug() << "new window";
398 installFacehuggers();
399 wi = findWindowInfo(mActiveWindow);
402 if (wi) {
403 qDebug() << "fixing window layout";
404 wi->layout = index;
405 if (jCfg->switching == APP_LAYOUT) {
406 wi = findAppInfo(mActiveWindow);
407 if (wi) {
408 qDebug() << "fixing app layout";
409 wi->layout = index;
410 } else {
411 qWarning() << "***APP SHIT!***";
414 } else {
415 qWarning() << "***WINDOW SHIT!***";
419 setDesktopLayout(X11Tools::activeDesktop(), mActiveGroup);
423 void Jrugr::setNextGroup () {
424 mXkb->setActiveGroup(mActiveGroup+1>=mGroupInfo.size()?0:mActiveGroup+1);
428 void Jrugr::setPrevGroup () {
429 mXkb->setActiveGroup(mActiveGroup-1<0?mGroupInfo.size()-1:mActiveGroup-1);
433 void Jrugr::windowDies (Window w) {
434 if (w != None) {
435 qDebug("**************************** FACEHUGGER PARENT DIES ****************************");
436 Window appW = None;
437 // remove window
439 QWIHashMutableIterator i(mKnownWindows);
440 while (i.hasNext()) {
441 WinInfo *wi = i.next().value();
443 if (wi->w == w) {
444 i.remove();
445 if (wi->w == wi->appWindow) {
446 if (appW == None) appW = wi->w;
447 else if (appW != wi->w) qWarning("*** WTF?! ***");
449 delete wi;
453 if (appW != None) {
454 // remove application
455 QWIHashMutableIterator i(mKnownApps);
456 while (i.hasNext()) {
457 WinInfo *wi = i.next().value();
459 if (wi->w == appW) {
460 i.remove();
461 delete wi;
469 void Jrugr::onChangeLayout (int index) {
470 qDebug() << "Jrugr::changeLayout:" << index;
471 mXkb->setActiveGroup(index);
475 void Jrugr::actionsActivate (QAction *action) {
476 QString cmd = action->data().toString();
478 qDebug() << "Jrugr::actionsActivate() command" << cmd;
479 if (cmd == "configure") {
480 configure();
481 } else if (cmd == "about_qt") {
482 aboutQt();
483 } else if (cmd == "about_jrugr") {
484 QMessageBox::about(0,
485 tr("About Jrugr"),
486 tr("<h2>Jrugr, the keyboard layout switcher</h2>"
487 "<b>Version</b>: %1<p>"
488 "Gui tool to configure XKB extentions of X server.<p>"
489 "(c) 2009-2011 Fedor Chelbarakh, Petr Vanek, Ketmar").arg(JRUGR_VERSION));
490 } else if (cmd == "exit") {
491 qDebug() << "Jrugr::actionsActivate() exit";
492 quit();
493 } else {
494 for (int f = mGroupInfo.size()-1; f >= 0; --f) {
495 if (cmd == mGroupInfo[f].name) {
496 emit changeLayout(f);
497 break;
504 #ifdef K8_MPLAYER_HACK
505 static QX11WindowList getPossibleActives (void) {
506 QX11WindowList res, wl = X11Tools::topLevelWindowStack();
507 int desk = X11Tools::activeDesktop();
508 Window wine = None;
510 //qDebug() << "looking for window to refocus...";
511 for (int f = 0; f < wl.size(); ++f) {
512 Window w = wl[f];
513 int wd = X11Tools::windowDesktop(w);
515 if (wd == desk) {
516 // skip 'special' windows
517 QStringList sl = X11Tools::windowStateNames(w);
518 if (sl.indexOf("_NET_WM_STATE_STICKY") < 0 &&
519 sl.indexOf("_NET_WM_STATE_HIDDEN") < 0 &&
520 sl.indexOf("_NET_WM_STATE_SHADED") < 0 &&
521 sl.indexOf("_NET_WM_STATE_SKIP_TASKBAR") < 0 &&
522 sl.indexOf("_NET_WM_STATE_ABOVE") < 0 &&
523 sl.indexOf("_NET_WM_STATE_BELOW") < 0) {
524 if (X11Tools::windowClass(w) == "xv") {
525 QString name = X11Tools::windowName(w).toLower();
526 if (name == "mplayer" || name == "mplayer2") continue;
528 if (wine == None && X11Tools::windowClass(w) == "explorer.exe") wine = w; else res << w;
533 if (wine != None) res << wine;
535 return res;
539 static bool deactivateMPlayer (void) {
540 qDebug() << "onActiveWindowChanged";
541 Window aw = X11Tools::activeWindow();
542 bool changeFocus = false;
544 if (aw != None) {
545 if (X11Tools::windowDesktop(aw) == -1 && X11Tools::windowClass(aw) == "xv" && X11Tools::windowName(aw) == "MPlayer") {
546 qDebug() << "MPlayer hack!";
547 changeFocus = true;
549 } else {
550 qDebug() << "Empty window hack!";
551 changeFocus = true;
554 if (changeFocus) {
555 // find previous window
556 QX11WindowList wl = getPossibleActives();
558 if (wl.size() > 0) {
559 Window pw = wl.at(wl.size()-1);
561 qDebug() << "CHANGING FOCUS!" <<
562 "class:" << X11Tools::windowClass(pw) <<
563 "name:" << X11Tools::windowName(pw) <<
564 "pid:" << X11Tools::windowPID(pw);
566 X11Tools::setActiveWindow(pw);
567 return true;
570 return false;
572 #endif
575 void Jrugr::onActiveWindowChanged (Window w) {
576 qDebug() << "onActiveWindowChanged";
577 if (w == None) {
578 #ifdef K8_MPLAYER_HACK
579 if (mDeskJustChanged) {
580 deactivateMPlayer();
582 #endif
583 mDeskJustChanged = false;
584 mActiveWindow = None;
585 qDebug() << "new active window: NONE";
586 return;
588 qDebug() << "new active window:" << (int)w << "desktop:" << X11Tools::windowDesktop(w);
589 dumpWinInfo("new active window:", w);
591 if (mDeskJustChanged) {
592 qDebug() << "*** mDeskJustChanged ***";
593 mDeskJustChanged = false;
594 #ifdef K8_MPLAYER_HACK
595 if (deactivateMPlayer()) return;
596 #endif
600 WinInfo *wi = findWindowInfo(w), *aw;
601 if (!wi) {
602 qDebug() << "new window";
603 installFacehuggers();
604 wi = findWindowInfo(w);
606 if (wi) {
607 switch (jCfg->switching) {
608 case APP_LAYOUT:
609 if ((aw = findAppInfo(w)) != findAppInfo(mActiveWindow)) {
610 qDebug("application changed!");
611 if (aw) {
612 if (aw->layout != mActiveGroup) {
613 qDebug("enforcing layout %d", aw->layout);
614 emit changeLayout(aw->layout);
616 } else {
617 qWarning("*** APP SHIT! ***");
620 break;
621 case WIN_LAYOUT:
622 if (wi->layout != mActiveGroup) {
623 qDebug("enforcing layout %d", wi->layout);
624 emit changeLayout(wi->layout);
626 break;
627 default: break;
631 mActiveWindow = w;
635 void Jrugr::onClientListChanged () {
636 qDebug() << "onClientListChanged";
637 installFacehuggers();
641 void Jrugr::onActiveDesktopChanged (int desk) {
642 qDebug() << "onActiveDesktopChanged:" << desk;
643 mDeskJustChanged = true;
644 #ifdef K8_MPLAYER_HACK
645 if (deactivateMPlayer()) mDeskJustChanged = false;
646 #endif
647 if (jCfg->switching == DESK_LAYOUT && mCurDesk != desk) {
648 int ll = desktopLayout(desk);
649 setDesktopLayout(desk, ll); // expand desktop list
650 if (ll != mActiveGroup) {
651 qDebug("enforce layout %d for desktop %d", ll, desk);
652 emit changeLayout(mDeskLangs.at(desk));
655 mCurDesk = desk;
659 bool Jrugr::x11EventFilter (XEvent *event) {
661 //qDebug() << "Jrugr::x11EventFilter:" << x11EventName(event);
662 switch (event->type) {
663 case DestroyNotify:
664 windowDies(event->xdestroywindow.window);
665 break;
666 case PropertyNotify:
667 if (event->xproperty.state == PropertyNewValue) {
668 if (atomActWin != None && event->xproperty.atom == atomActWin) {
669 //qDebug() << "emit: activeWindowChanged";
670 emit activeWindowChanged();
672 if (atomCurDesk != None && event->xproperty.atom == atomCurDesk) {
673 //qDebug() << "emit: activeDesktopChanged";
674 emit activeDesktopChanged();
676 if (atomClientList != None && event->xproperty.atom == atomClientList) {
677 qDebug() << "emit: clientListChanged";
678 emit clientListChanged();
681 //qDebug() << "PropertyNotify complete";
682 break;
683 default:
684 mXkb->processEvent(event);
685 //return false;
686 break;
688 return false; // normal dispatching