Changeset View
Changeset View
Standalone View
Standalone View
x11client.cpp
Show All 25 Lines | |||||
26 | #endif | 26 | #endif | ||
27 | #include "atoms.h" | 27 | #include "atoms.h" | ||
28 | #include "client_machine.h" | 28 | #include "client_machine.h" | ||
29 | #include "composite.h" | 29 | #include "composite.h" | ||
30 | #include "cursor.h" | 30 | #include "cursor.h" | ||
31 | #include "deleted.h" | 31 | #include "deleted.h" | ||
32 | #include "focuschain.h" | 32 | #include "focuschain.h" | ||
33 | #include "group.h" | 33 | #include "group.h" | ||
34 | #include "netinfo.h" | ||||
34 | #include "screens.h" | 35 | #include "screens.h" | ||
35 | #include "shadow.h" | 36 | #include "shadow.h" | ||
36 | #ifdef KWIN_BUILD_TABBOX | 37 | #ifdef KWIN_BUILD_TABBOX | ||
37 | #include "tabbox.h" | 38 | #include "tabbox.h" | ||
38 | #endif | 39 | #endif | ||
39 | #include "workspace.h" | 40 | #include "workspace.h" | ||
40 | #include "screenedge.h" | 41 | #include "screenedge.h" | ||
41 | #include "decorations/decorationbridge.h" | 42 | #include "decorations/decorationbridge.h" | ||
42 | #include "decorations/decoratedclient.h" | 43 | #include "decorations/decoratedclient.h" | ||
43 | #include <KDecoration2/Decoration> | 44 | #include <KDecoration2/Decoration> | ||
44 | #include <KDecoration2/DecoratedClient> | 45 | #include <KDecoration2/DecoratedClient> | ||
45 | // KDE | 46 | // KDE | ||
46 | #include <KLocalizedString> | 47 | #include <KLocalizedString> | ||
48 | #include <KStartupInfo> | ||||
47 | #include <KWindowSystem> | 49 | #include <KWindowSystem> | ||
48 | #include <KColorScheme> | 50 | #include <KColorScheme> | ||
49 | // Qt | 51 | // Qt | ||
50 | #include <QApplication> | 52 | #include <QApplication> | ||
51 | #include <QDebug> | 53 | #include <QDebug> | ||
52 | #include <QDir> | 54 | #include <QDir> | ||
53 | #include <QFile> | 55 | #include <QFile> | ||
54 | #include <QFileInfo> | 56 | #include <QFileInfo> | ||
▲ Show 20 Lines • Show All 260 Lines • ▼ Show 20 Line(s) | 295 | #endif | |||
315 | m_wrapper.reset(); | 317 | m_wrapper.reset(); | ||
316 | m_frame.reset(); | 318 | m_frame.reset(); | ||
317 | unblockGeometryUpdates(); // Don't use GeometryUpdatesBlocker, it would now set the geometry | 319 | unblockGeometryUpdates(); // Don't use GeometryUpdatesBlocker, it would now set the geometry | ||
318 | disownDataPassedToDeleted(); | 320 | disownDataPassedToDeleted(); | ||
319 | del->unrefWindow(); | 321 | del->unrefWindow(); | ||
320 | deleteClient(this); | 322 | deleteClient(this); | ||
321 | } | 323 | } | ||
322 | 324 | | |||
325 | /** | ||||
326 | * Manages the clients. This means handling the very first maprequest: | ||||
327 | * reparenting, initial geometry, initial state, placement, etc. | ||||
328 | * Returns false if KWin is not going to manage this window. | ||||
329 | */ | ||||
330 | bool X11Client::manage(xcb_window_t w, bool isMapped) | ||||
331 | { | ||||
332 | StackingUpdatesBlocker stacking_blocker(workspace()); | ||||
333 | | ||||
334 | Xcb::WindowAttributes attr(w); | ||||
335 | Xcb::WindowGeometry windowGeometry(w); | ||||
336 | if (attr.isNull() || windowGeometry.isNull()) { | ||||
337 | return false; | ||||
338 | } | ||||
339 | | ||||
340 | // From this place on, manage() must not return false | ||||
341 | blockGeometryUpdates(); | ||||
342 | setPendingGeometryUpdate(PendingGeometryForced); // Force update when finishing with geometry changes | ||||
343 | | ||||
344 | embedClient(w, attr->visual, attr->colormap, windowGeometry->depth); | ||||
345 | | ||||
346 | m_visual = attr->visual; | ||||
347 | bit_depth = windowGeometry->depth; | ||||
348 | | ||||
349 | // SELI TODO: Order all these things in some sane manner | ||||
350 | | ||||
351 | const NET::Properties properties = | ||||
352 | NET::WMDesktop | | ||||
353 | NET::WMState | | ||||
354 | NET::WMWindowType | | ||||
355 | NET::WMStrut | | ||||
356 | NET::WMName | | ||||
357 | NET::WMIconGeometry | | ||||
358 | NET::WMIcon | | ||||
359 | NET::WMPid | | ||||
360 | NET::WMIconName; | ||||
361 | const NET::Properties2 properties2 = | ||||
362 | NET::WM2BlockCompositing | | ||||
363 | NET::WM2WindowClass | | ||||
364 | NET::WM2WindowRole | | ||||
365 | NET::WM2UserTime | | ||||
366 | NET::WM2StartupId | | ||||
367 | NET::WM2ExtendedStrut | | ||||
368 | NET::WM2Opacity | | ||||
369 | NET::WM2FullscreenMonitors | | ||||
370 | NET::WM2FrameOverlap | | ||||
371 | NET::WM2GroupLeader | | ||||
372 | NET::WM2Urgency | | ||||
373 | NET::WM2Input | | ||||
374 | NET::WM2Protocols | | ||||
375 | NET::WM2InitialMappingState | | ||||
376 | NET::WM2IconPixmap | | ||||
377 | NET::WM2OpaqueRegion | | ||||
378 | NET::WM2DesktopFileName | | ||||
379 | NET::WM2GTKFrameExtents; | ||||
380 | | ||||
381 | auto wmClientLeaderCookie = fetchWmClientLeader(); | ||||
382 | auto skipCloseAnimationCookie = fetchSkipCloseAnimation(); | ||||
383 | auto showOnScreenEdgeCookie = fetchShowOnScreenEdge(); | ||||
384 | auto colorSchemeCookie = fetchColorScheme(); | ||||
385 | auto firstInTabBoxCookie = fetchFirstInTabBox(); | ||||
386 | auto transientCookie = fetchTransient(); | ||||
387 | auto activitiesCookie = fetchActivities(); | ||||
388 | auto applicationMenuServiceNameCookie = fetchApplicationMenuServiceName(); | ||||
389 | auto applicationMenuObjectPathCookie = fetchApplicationMenuObjectPath(); | ||||
390 | | ||||
391 | m_geometryHints.init(window()); | ||||
392 | m_motif.init(window()); | ||||
393 | info = new WinInfo(this, m_client, rootWindow(), properties, properties2); | ||||
394 | | ||||
395 | if (isDesktop() && bit_depth == 32) { | ||||
396 | // force desktop windows to be opaque. It's a desktop after all, there is no window below | ||||
397 | bit_depth = 24; | ||||
398 | } | ||||
399 | | ||||
400 | // If it's already mapped, ignore hint | ||||
401 | bool init_minimize = !isMapped && (info->initialMappingState() == NET::Iconic); | ||||
402 | | ||||
403 | m_colormap = attr->colormap; | ||||
404 | | ||||
405 | getResourceClass(); | ||||
406 | readWmClientLeader(wmClientLeaderCookie); | ||||
407 | getWmClientMachine(); | ||||
408 | getSyncCounter(); | ||||
409 | // First only read the caption text, so that setupWindowRules() can use it for matching, | ||||
410 | // and only then really set the caption using setCaption(), which checks for duplicates etc. | ||||
411 | // and also relies on rules already existing | ||||
412 | cap_normal = readName(); | ||||
413 | setupWindowRules(false); | ||||
414 | setCaption(cap_normal, true); | ||||
415 | | ||||
416 | connect(this, &X11Client::windowClassChanged, this, &X11Client::evaluateWindowRules); | ||||
417 | | ||||
418 | if (Xcb::Extensions::self()->isShapeAvailable()) | ||||
419 | xcb_shape_select_input(connection(), window(), true); | ||||
420 | detectShape(window()); | ||||
421 | detectNoBorder(); | ||||
422 | fetchIconicName(); | ||||
423 | setClientFrameExtents(info->gtkFrameExtents()); | ||||
424 | | ||||
425 | // Needs to be done before readTransient() because of reading the group | ||||
426 | checkGroup(); | ||||
427 | updateUrgency(); | ||||
428 | updateAllowedActions(); // Group affects isMinimizable() | ||||
429 | | ||||
430 | setModal((info->state() & NET::Modal) != 0); // Needs to be valid before handling groups | ||||
431 | readTransientProperty(transientCookie); | ||||
432 | setDesktopFileName(rules()->checkDesktopFile(QByteArray(info->desktopFileName()), true).toUtf8()); | ||||
433 | getIcons(); | ||||
434 | connect(this, &X11Client::desktopFileNameChanged, this, &X11Client::getIcons); | ||||
435 | | ||||
436 | m_geometryHints.read(); | ||||
437 | getMotifHints(); | ||||
438 | getWmOpaqueRegion(); | ||||
439 | readSkipCloseAnimation(skipCloseAnimationCookie); | ||||
440 | | ||||
441 | // TODO: Try to obey all state information from info->state() | ||||
442 | | ||||
443 | setOriginalSkipTaskbar((info->state() & NET::SkipTaskbar) != 0); | ||||
444 | setSkipPager((info->state() & NET::SkipPager) != 0); | ||||
445 | setSkipSwitcher((info->state() & NET::SkipSwitcher) != 0); | ||||
446 | readFirstInTabBox(firstInTabBoxCookie); | ||||
447 | | ||||
448 | setupCompositing(); | ||||
449 | | ||||
450 | KStartupInfoId asn_id; | ||||
451 | KStartupInfoData asn_data; | ||||
452 | bool asn_valid = workspace()->checkStartupNotification(window(), asn_id, asn_data); | ||||
453 | | ||||
454 | // Make sure that the input window is created before we update the stacking order | ||||
455 | updateInputWindow(); | ||||
456 | | ||||
457 | workspace()->updateClientLayer(this); | ||||
458 | | ||||
459 | SessionInfo* session = workspace()->takeSessionInfo(this); | ||||
460 | if (session) { | ||||
461 | init_minimize = session->minimized; | ||||
462 | noborder = session->noBorder; | ||||
463 | } | ||||
464 | | ||||
465 | setShortcut(rules()->checkShortcut(session ? session->shortcut : QString(), true)); | ||||
466 | | ||||
467 | init_minimize = rules()->checkMinimize(init_minimize, !isMapped); | ||||
468 | noborder = rules()->checkNoBorder(noborder, !isMapped); | ||||
469 | | ||||
470 | readActivities(activitiesCookie); | ||||
471 | | ||||
472 | // Initial desktop placement | ||||
473 | int desk = 0; | ||||
474 | if (session) { | ||||
475 | desk = session->desktop; | ||||
476 | if (session->onAllDesktops) | ||||
477 | desk = NET::OnAllDesktops; | ||||
478 | setOnActivities(session->activities); | ||||
479 | } else { | ||||
480 | // If this window is transient, ensure that it is opened on the | ||||
481 | // same window as its parent. this is necessary when an application | ||||
482 | // starts up on a different desktop than is currently displayed | ||||
483 | if (isTransient()) { | ||||
484 | auto mainclients = mainClients(); | ||||
485 | bool on_current = false; | ||||
486 | bool on_all = false; | ||||
487 | AbstractClient* maincl = nullptr; | ||||
488 | // This is slightly duplicated from Placement::placeOnMainWindow() | ||||
489 | for (auto it = mainclients.constBegin(); | ||||
490 | it != mainclients.constEnd(); | ||||
491 | ++it) { | ||||
492 | if (mainclients.count() > 1 && // A group-transient | ||||
493 | (*it)->isSpecialWindow() && // Don't consider toolbars etc when placing | ||||
494 | !(info->state() & NET::Modal)) // except when it's modal (blocks specials as well) | ||||
495 | continue; | ||||
496 | maincl = *it; | ||||
497 | if ((*it)->isOnCurrentDesktop()) | ||||
498 | on_current = true; | ||||
499 | if ((*it)->isOnAllDesktops()) | ||||
500 | on_all = true; | ||||
501 | } | ||||
502 | if (on_all) | ||||
503 | desk = NET::OnAllDesktops; | ||||
504 | else if (on_current) | ||||
505 | desk = VirtualDesktopManager::self()->current(); | ||||
506 | else if (maincl != nullptr) | ||||
507 | desk = maincl->desktop(); | ||||
508 | | ||||
509 | if (maincl) | ||||
510 | setOnActivities(maincl->activities()); | ||||
511 | } else { // a transient shall appear on its leader and not drag that around | ||||
512 | if (info->desktop()) | ||||
513 | desk = info->desktop(); // Window had the initial desktop property, force it | ||||
514 | if (desktop() == 0 && asn_valid && asn_data.desktop() != 0) | ||||
515 | desk = asn_data.desktop(); | ||||
516 | } | ||||
517 | #ifdef KWIN_BUILD_ACTIVITIES | ||||
518 | if (Activities::self() && !isMapped && !noborder && isNormalWindow() && !activitiesDefined) { | ||||
519 | //a new, regular window, when we're not recovering from a crash, | ||||
520 | //and it hasn't got an activity. let's try giving it the current one. | ||||
521 | //TODO: decide whether to keep this before the 4.6 release | ||||
522 | //TODO: if we are keeping it (at least as an option), replace noborder checking | ||||
523 | //with a public API for setting windows to be on all activities. | ||||
524 | //something like KWindowSystem::setOnAllActivities or | ||||
525 | //KActivityConsumer::setOnAllActivities | ||||
526 | setOnActivity(Activities::self()->current(), true); | ||||
527 | } | ||||
528 | #endif | ||||
529 | } | ||||
530 | | ||||
531 | if (desk == 0) // Assume window wants to be visible on the current desktop | ||||
532 | desk = isDesktop() ? static_cast<int>(NET::OnAllDesktops) : VirtualDesktopManager::self()->current(); | ||||
533 | desk = rules()->checkDesktop(desk, !isMapped); | ||||
534 | if (desk != NET::OnAllDesktops) // Do range check | ||||
535 | desk = qBound(1, desk, static_cast<int>(VirtualDesktopManager::self()->count())); | ||||
536 | setDesktop(desk); | ||||
537 | info->setDesktop(desk); | ||||
538 | workspace()->updateOnAllDesktopsOfTransients(this); // SELI TODO | ||||
539 | //onAllDesktopsChange(); // Decoration doesn't exist here yet | ||||
540 | | ||||
541 | QString activitiesList; | ||||
542 | activitiesList = rules()->checkActivity(activitiesList, !isMapped); | ||||
543 | if (!activitiesList.isEmpty()) | ||||
544 | setOnActivities(activitiesList.split(QStringLiteral(","))); | ||||
545 | | ||||
546 | QRect geom(windowGeometry.rect()); | ||||
547 | bool placementDone = false; | ||||
548 | | ||||
549 | if (session) | ||||
550 | geom = session->geometry; | ||||
551 | | ||||
552 | QRect area; | ||||
553 | bool partial_keep_in_area = isMapped || session; | ||||
554 | if (isMapped || session) { | ||||
555 | area = workspace()->clientArea(FullArea, geom.center(), desktop()); | ||||
556 | checkOffscreenPosition(&geom, area); | ||||
557 | } else { | ||||
558 | int screen = asn_data.xinerama() == -1 ? screens()->current() : asn_data.xinerama(); | ||||
559 | screen = rules()->checkScreen(screen, !isMapped); | ||||
560 | area = workspace()->clientArea(PlacementArea, screens()->geometry(screen).center(), desktop()); | ||||
561 | } | ||||
562 | | ||||
563 | if (isDesktop()) | ||||
564 | // KWin doesn't manage desktop windows | ||||
565 | placementDone = true; | ||||
566 | | ||||
567 | bool usePosition = false; | ||||
568 | if (isMapped || session || placementDone) | ||||
569 | placementDone = true; // Use geometry | ||||
570 | else if (isTransient() && !isUtility() && !isDialog() && !isSplash()) | ||||
571 | usePosition = true; | ||||
572 | else if (isTransient() && !hasNETSupport()) | ||||
573 | usePosition = true; | ||||
574 | else if (isDialog() && hasNETSupport()) { | ||||
575 | // If the dialog is actually non-NETWM transient window, don't try to apply placement to it, | ||||
576 | // it breaks with too many things (xmms, display) | ||||
577 | if (mainClients().count() >= 1) { | ||||
578 | #if 1 | ||||
579 | // #78082 - Ok, it seems there are after all some cases when an application has a good | ||||
580 | // reason to specify a position for its dialog. Too bad other WMs have never bothered | ||||
581 | // with placement for dialogs, so apps always specify positions for their dialogs, | ||||
582 | // including such silly positions like always centered on the screen or under mouse. | ||||
583 | // Using ignoring requested position in window-specific settings helps, and now | ||||
584 | // there's also _NET_WM_FULL_PLACEMENT. | ||||
585 | usePosition = true; | ||||
586 | #else | ||||
587 | ; // Force using placement policy | ||||
588 | #endif | ||||
589 | } else | ||||
590 | usePosition = true; | ||||
591 | } else if (isSplash()) | ||||
592 | ; // Force using placement policy | ||||
593 | else | ||||
594 | usePosition = true; | ||||
595 | if (!rules()->checkIgnoreGeometry(!usePosition, true)) { | ||||
596 | if (m_geometryHints.hasPosition()) { | ||||
597 | placementDone = true; | ||||
598 | // Disobey xinerama placement option for now (#70943) | ||||
599 | area = workspace()->clientArea(PlacementArea, geom.center(), desktop()); | ||||
600 | } | ||||
601 | } | ||||
602 | | ||||
603 | if (isMovable() && (geom.x() > area.right() || geom.y() > area.bottom())) | ||||
604 | placementDone = false; // Weird, do not trust. | ||||
605 | | ||||
606 | if (placementDone) { | ||||
607 | QPoint position = geom.topLeft(); | ||||
608 | // Session contains the position of the frame geometry before gravitating. | ||||
609 | if (!session) { | ||||
610 | position = clientPosToFramePos(position); | ||||
611 | } | ||||
612 | move(position); | ||||
613 | } | ||||
614 | | ||||
615 | // Create client group if the window will have a decoration | ||||
616 | bool dontKeepInArea = false; | ||||
617 | readColorScheme(colorSchemeCookie); | ||||
618 | | ||||
619 | readApplicationMenuServiceName(applicationMenuServiceNameCookie); | ||||
620 | readApplicationMenuObjectPath(applicationMenuObjectPathCookie); | ||||
621 | | ||||
622 | updateDecoration(false); // Also gravitates | ||||
623 | // TODO: Is CentralGravity right here, when resizing is done after gravitating? | ||||
624 | plainResize(rules()->checkSize(sizeForClientSize(geom.size()), !isMapped)); | ||||
625 | | ||||
626 | QPoint forced_pos = rules()->checkPosition(invalidPoint, !isMapped); | ||||
627 | if (forced_pos != invalidPoint) { | ||||
628 | move(forced_pos); | ||||
629 | placementDone = true; | ||||
630 | // Don't keep inside workarea if the window has specially configured position | ||||
631 | partial_keep_in_area = true; | ||||
632 | area = workspace()->clientArea(FullArea, geom.center(), desktop()); | ||||
633 | } | ||||
634 | if (!placementDone) { | ||||
635 | // Placement needs to be after setting size | ||||
636 | Placement::self()->place(this, area); | ||||
637 | // The client may have been moved to another screen, update placement area. | ||||
638 | area = workspace()->clientArea(PlacementArea, this); | ||||
639 | dontKeepInArea = true; | ||||
640 | placementDone = true; | ||||
641 | } | ||||
642 | | ||||
643 | // bugs #285967, #286146, #183694 | ||||
644 | // geometry() now includes the requested size and the decoration and is at the correct screen/position (hopefully) | ||||
645 | // Maximization for oversized windows must happen NOW. | ||||
646 | // If we effectively pass keepInArea(), the window will resizeWithChecks() - i.e. constrained | ||||
647 | // to the combo of all screen MINUS all struts on the edges | ||||
648 | // If only one screen struts, this will affect screens as a side-effect, the window is artificailly shrinked | ||||
649 | // below the screen size and as result no more maximized what breaks KMainWindow's stupid width+1, height+1 hack | ||||
650 | // TODO: get KMainWindow a correct state storage what will allow to store the restore size as well. | ||||
651 | | ||||
652 | if (!session) { // has a better handling of this | ||||
653 | geom_restore = frameGeometry(); // Remember restore geometry | ||||
654 | if (isMaximizable() && (width() >= area.width() || height() >= area.height())) { | ||||
655 | // Window is too large for the screen, maximize in the | ||||
656 | // directions necessary | ||||
657 | const QSize ss = workspace()->clientArea(ScreenArea, area.center(), desktop()).size(); | ||||
658 | const QRect fsa = workspace()->clientArea(FullArea, geom.center(), desktop()); | ||||
659 | const QSize cs = clientSize(); | ||||
660 | int pseudo_max = ((info->state() & NET::MaxVert) ? MaximizeVertical : 0) | | ||||
661 | ((info->state() & NET::MaxHoriz) ? MaximizeHorizontal : 0); | ||||
662 | if (width() >= area.width()) | ||||
663 | pseudo_max |= MaximizeHorizontal; | ||||
664 | if (height() >= area.height()) | ||||
665 | pseudo_max |= MaximizeVertical; | ||||
666 | | ||||
667 | // heuristics: | ||||
668 | // if decorated client is smaller than the entire screen, the user might want to move it around (multiscreen) | ||||
669 | // in this case, if the decorated client is bigger than the screen (+1), we don't take this as an | ||||
670 | // attempt for maximization, but just constrain the size (the window simply wants to be bigger) | ||||
671 | // NOTICE | ||||
672 | // i intended a second check on cs < area.size() ("the managed client ("minus border") is smaller | ||||
673 | // than the workspace") but gtk / gimp seems to store it's size including the decoration, | ||||
674 | // thus a former maximized window wil become non-maximized | ||||
675 | bool keepInFsArea = false; | ||||
676 | if (width() < fsa.width() && (cs.width() > ss.width()+1)) { | ||||
677 | pseudo_max &= ~MaximizeHorizontal; | ||||
678 | keepInFsArea = true; | ||||
679 | } | ||||
680 | if (height() < fsa.height() && (cs.height() > ss.height()+1)) { | ||||
681 | pseudo_max &= ~MaximizeVertical; | ||||
682 | keepInFsArea = true; | ||||
683 | } | ||||
684 | | ||||
685 | if (pseudo_max != MaximizeRestore) { | ||||
686 | maximize((MaximizeMode)pseudo_max); | ||||
687 | // from now on, care about maxmode, since the maximization call will override mode for fix aspects | ||||
688 | dontKeepInArea |= (max_mode == MaximizeFull); | ||||
689 | geom_restore = QRect(); // Use placement when unmaximizing ... | ||||
690 | if (!(max_mode & MaximizeVertical)) { | ||||
691 | geom_restore.setY(y()); // ...but only for horizontal direction | ||||
692 | geom_restore.setHeight(height()); | ||||
693 | } | ||||
694 | if (!(max_mode & MaximizeHorizontal)) { | ||||
695 | geom_restore.setX(x()); // ...but only for vertical direction | ||||
696 | geom_restore.setWidth(width()); | ||||
697 | } | ||||
698 | } | ||||
699 | if (keepInFsArea) | ||||
700 | keepInArea(fsa, partial_keep_in_area); | ||||
701 | } | ||||
702 | } | ||||
703 | | ||||
704 | if ((!isSpecialWindow() || isToolbar()) && isMovable() && !dontKeepInArea) | ||||
705 | keepInArea(area, partial_keep_in_area); | ||||
706 | | ||||
707 | updateShape(); | ||||
708 | | ||||
709 | // CT: Extra check for stupid jdk 1.3.1. But should make sense in general | ||||
710 | // if client has initial state set to Iconic and is transient with a parent | ||||
711 | // window that is not Iconic, set init_state to Normal | ||||
712 | if (init_minimize && isTransient()) { | ||||
713 | auto mainclients = mainClients(); | ||||
714 | for (auto it = mainclients.constBegin(); | ||||
715 | it != mainclients.constEnd(); | ||||
716 | ++it) | ||||
717 | if ((*it)->isShown(true)) | ||||
718 | init_minimize = false; // SELI TODO: Even e.g. for NET::Utility? | ||||
719 | } | ||||
720 | // If a dialog is shown for minimized window, minimize it too | ||||
721 | if (!init_minimize && isTransient() && mainClients().count() > 0 && | ||||
722 | workspace()->sessionManager()->state() != SessionState::Saving) { | ||||
723 | bool visible_parent = false; | ||||
724 | // Use allMainClients(), to include also main clients of group transients | ||||
725 | // that have been optimized out in X11Client::checkGroupTransients() | ||||
726 | auto mainclients = allMainClients(); | ||||
727 | for (auto it = mainclients.constBegin(); | ||||
728 | it != mainclients.constEnd(); | ||||
729 | ++it) | ||||
730 | if ((*it)->isShown(true)) | ||||
731 | visible_parent = true; | ||||
732 | if (!visible_parent) { | ||||
733 | init_minimize = true; | ||||
734 | demandAttention(); | ||||
735 | } | ||||
736 | } | ||||
737 | | ||||
738 | if (init_minimize) | ||||
739 | minimize(true); // No animation | ||||
740 | | ||||
741 | // Other settings from the previous session | ||||
742 | if (session) { | ||||
743 | // Session restored windows are not considered to be new windows WRT rules, | ||||
744 | // I.e. obey only forcing rules | ||||
745 | setKeepAbove(session->keepAbove); | ||||
746 | setKeepBelow(session->keepBelow); | ||||
747 | setOriginalSkipTaskbar(session->skipTaskbar); | ||||
748 | setSkipPager(session->skipPager); | ||||
749 | setSkipSwitcher(session->skipSwitcher); | ||||
750 | setShade(session->shaded ? ShadeNormal : ShadeNone); | ||||
751 | setOpacity(session->opacity); | ||||
752 | geom_restore = session->restore; | ||||
753 | if (session->maximized != MaximizeRestore) { | ||||
754 | maximize(MaximizeMode(session->maximized)); | ||||
755 | } | ||||
756 | if (session->fullscreen != FullScreenNone) { | ||||
757 | setFullScreen(true, false); | ||||
758 | geom_fs_restore = session->fsrestore; | ||||
759 | } | ||||
760 | checkOffscreenPosition(&geom_restore, area); | ||||
761 | checkOffscreenPosition(&geom_fs_restore, area); | ||||
762 | } else { | ||||
763 | // Window may want to be maximized | ||||
764 | // done after checking that the window isn't larger than the workarea, so that | ||||
765 | // the restore geometry from the checks above takes precedence, and window | ||||
766 | // isn't restored larger than the workarea | ||||
767 | MaximizeMode maxmode = static_cast<MaximizeMode>( | ||||
768 | ((info->state() & NET::MaxVert) ? MaximizeVertical : 0) | | ||||
769 | ((info->state() & NET::MaxHoriz) ? MaximizeHorizontal : 0)); | ||||
770 | MaximizeMode forced_maxmode = rules()->checkMaximize(maxmode, !isMapped); | ||||
771 | | ||||
772 | // Either hints were set to maximize, or is forced to maximize, | ||||
773 | // or is forced to non-maximize and hints were set to maximize | ||||
774 | if (forced_maxmode != MaximizeRestore || maxmode != MaximizeRestore) | ||||
775 | maximize(forced_maxmode); | ||||
776 | | ||||
777 | // Read other initial states | ||||
778 | setShade(rules()->checkShade(info->state() & NET::Shaded ? ShadeNormal : ShadeNone, !isMapped)); | ||||
779 | setKeepAbove(rules()->checkKeepAbove(info->state() & NET::KeepAbove, !isMapped)); | ||||
780 | setKeepBelow(rules()->checkKeepBelow(info->state() & NET::KeepBelow, !isMapped)); | ||||
781 | setOriginalSkipTaskbar(rules()->checkSkipTaskbar(info->state() & NET::SkipTaskbar, !isMapped)); | ||||
782 | setSkipPager(rules()->checkSkipPager(info->state() & NET::SkipPager, !isMapped)); | ||||
783 | setSkipSwitcher(rules()->checkSkipSwitcher(info->state() & NET::SkipSwitcher, !isMapped)); | ||||
784 | if (info->state() & NET::DemandsAttention) | ||||
785 | demandAttention(); | ||||
786 | if (info->state() & NET::Modal) | ||||
787 | setModal(true); | ||||
788 | | ||||
789 | setFullScreen(rules()->checkFullScreen(info->state() & NET::FullScreen, !isMapped), false); | ||||
790 | } | ||||
791 | | ||||
792 | updateAllowedActions(true); | ||||
793 | | ||||
794 | // Set initial user time directly | ||||
795 | m_userTime = readUserTimeMapTimestamp(asn_valid ? &asn_id : nullptr, asn_valid ? &asn_data : nullptr, session); | ||||
796 | group()->updateUserTime(m_userTime); // And do what X11Client::updateUserTime() does | ||||
797 | | ||||
798 | // This should avoid flicker, because real restacking is done | ||||
799 | // only after manage() finishes because of blocking, but the window is shown sooner | ||||
800 | m_frame.lower(); | ||||
801 | if (session && session->stackingOrder != -1) { | ||||
802 | sm_stacking_order = session->stackingOrder; | ||||
803 | workspace()->restoreSessionStackingOrder(this); | ||||
804 | } | ||||
805 | | ||||
806 | if (compositing()) | ||||
807 | // Sending ConfigureNotify is done when setting mapping state below, | ||||
808 | // Getting the first sync response means window is ready for compositing | ||||
809 | sendSyncRequest(); | ||||
810 | else | ||||
811 | ready_for_painting = true; // set to true in case compositing is turned on later. bug #160393 | ||||
812 | | ||||
813 | if (isShown(true)) { | ||||
814 | bool allow; | ||||
815 | if (session) | ||||
816 | allow = session->active && | ||||
817 | (!workspace()->wasUserInteraction() || workspace()->activeClient() == nullptr || | ||||
818 | workspace()->activeClient()->isDesktop()); | ||||
819 | else | ||||
820 | allow = workspace()->allowClientActivation(this, userTime(), false); | ||||
821 | | ||||
822 | const bool isSessionSaving = workspace()->sessionManager()->state() == SessionState::Saving; | ||||
823 | | ||||
824 | // If session saving, force showing new windows (i.e. "save file?" dialogs etc.) | ||||
825 | // also force if activation is allowed | ||||
826 | if( !isOnCurrentDesktop() && !isMapped && !session && ( allow || isSessionSaving )) | ||||
827 | VirtualDesktopManager::self()->setCurrent( desktop()); | ||||
828 | | ||||
829 | // If the window is on an inactive activity during session saving, temporarily force it to show. | ||||
830 | if( !isMapped && !session && isSessionSaving && !isOnCurrentActivity()) { | ||||
831 | setSessionActivityOverride( true ); | ||||
832 | foreach( AbstractClient* c, mainClients()) { | ||||
833 | if (X11Client *mc = dynamic_cast<X11Client *>(c)) { | ||||
834 | mc->setSessionActivityOverride(true); | ||||
835 | } | ||||
836 | } | ||||
837 | } | ||||
838 | | ||||
839 | if (isOnCurrentDesktop() && !isMapped && !allow && (!session || session->stackingOrder < 0)) | ||||
840 | workspace()->restackClientUnderActive(this); | ||||
841 | | ||||
842 | updateVisibility(); | ||||
843 | | ||||
844 | if (!isMapped) { | ||||
845 | if (allow && isOnCurrentDesktop()) { | ||||
846 | if (!isSpecialWindow()) | ||||
847 | if (options->focusPolicyIsReasonable() && wantsTabFocus()) | ||||
848 | workspace()->requestFocus(this); | ||||
849 | } else if (!session && !isSpecialWindow()) | ||||
850 | demandAttention(); | ||||
851 | } | ||||
852 | } else | ||||
853 | updateVisibility(); | ||||
854 | Q_ASSERT(mapping_state != Withdrawn); | ||||
855 | m_managed = true; | ||||
856 | blockGeometryUpdates(false); | ||||
857 | | ||||
858 | if (m_userTime == XCB_TIME_CURRENT_TIME || m_userTime == -1U) { | ||||
859 | // No known user time, set something old | ||||
860 | m_userTime = xTime() - 1000000; | ||||
861 | if (m_userTime == XCB_TIME_CURRENT_TIME || m_userTime == -1U) // Let's be paranoid | ||||
862 | m_userTime = xTime() - 1000000 + 10; | ||||
863 | } | ||||
864 | | ||||
865 | //sendSyntheticConfigureNotify(); // Done when setting mapping state | ||||
866 | | ||||
867 | delete session; | ||||
868 | | ||||
869 | discardTemporaryRules(); | ||||
870 | applyWindowRules(); // Just in case | ||||
871 | RuleBook::self()->discardUsed(this, false); // Remove ApplyNow rules | ||||
872 | updateWindowRules(Rules::All); // Was blocked while !isManaged() | ||||
873 | | ||||
874 | setBlockingCompositing(info->isBlockingCompositing()); | ||||
875 | readShowOnScreenEdge(showOnScreenEdgeCookie); | ||||
876 | | ||||
877 | // Forward all opacity values to the frame in case there'll be other CM running. | ||||
878 | connect(Compositor::self(), &Compositor::compositingToggled, this, | ||||
879 | [this](bool active) { | ||||
880 | if (active) { | ||||
881 | return; | ||||
882 | } | ||||
883 | if (opacity() == 1.0) { | ||||
884 | return; | ||||
885 | } | ||||
886 | NETWinInfo info(connection(), frameId(), rootWindow(), NET::Properties(), NET::Properties2()); | ||||
887 | info.setOpacity(static_cast<unsigned long>(opacity() * 0xffffffff)); | ||||
888 | } | ||||
889 | ); | ||||
890 | | ||||
891 | // TODO: there's a small problem here - isManaged() depends on the mapping state, | ||||
892 | // but this client is not yet in Workspace's client list at this point, will | ||||
893 | // be only done in addClient() | ||||
894 | emit clientManaging(this); | ||||
895 | return true; | ||||
896 | } | ||||
897 | | ||||
898 | // Called only from manage() | ||||
899 | void X11Client::embedClient(xcb_window_t w, xcb_visualid_t visualid, xcb_colormap_t colormap, uint8_t depth) | ||||
900 | { | ||||
901 | Q_ASSERT(m_client == XCB_WINDOW_NONE); | ||||
902 | Q_ASSERT(frameId() == XCB_WINDOW_NONE); | ||||
903 | Q_ASSERT(m_wrapper == XCB_WINDOW_NONE); | ||||
904 | m_client.reset(w, false); | ||||
905 | | ||||
906 | const uint32_t zero_value = 0; | ||||
907 | | ||||
908 | xcb_connection_t *conn = connection(); | ||||
909 | | ||||
910 | // We don't want the window to be destroyed when we quit | ||||
911 | xcb_change_save_set(conn, XCB_SET_MODE_INSERT, m_client); | ||||
912 | | ||||
913 | m_client.selectInput(zero_value); | ||||
914 | m_client.unmap(); | ||||
915 | m_client.setBorderWidth(zero_value); | ||||
916 | | ||||
917 | // Note: These values must match the order in the xcb_cw_t enum | ||||
918 | const uint32_t cw_values[] = { | ||||
919 | 0, // back_pixmap | ||||
920 | 0, // border_pixel | ||||
921 | colormap, // colormap | ||||
922 | Cursor::x11Cursor(Qt::ArrowCursor) | ||||
923 | }; | ||||
924 | | ||||
925 | const uint32_t cw_mask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | | ||||
926 | XCB_CW_COLORMAP | XCB_CW_CURSOR; | ||||
927 | | ||||
928 | const uint32_t common_event_mask = XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | | ||||
929 | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | | ||||
930 | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | | ||||
931 | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_POINTER_MOTION | | ||||
932 | XCB_EVENT_MASK_KEYMAP_STATE | | ||||
933 | XCB_EVENT_MASK_FOCUS_CHANGE | | ||||
934 | XCB_EVENT_MASK_EXPOSURE | | ||||
935 | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; | ||||
936 | | ||||
937 | const uint32_t frame_event_mask = common_event_mask | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_VISIBILITY_CHANGE; | ||||
938 | const uint32_t wrapper_event_mask = common_event_mask | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY; | ||||
939 | | ||||
940 | const uint32_t client_event_mask = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE | | ||||
941 | XCB_EVENT_MASK_COLOR_MAP_CHANGE | | ||||
942 | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | | ||||
943 | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE; | ||||
944 | | ||||
945 | // Create the frame window | ||||
946 | xcb_window_t frame = xcb_generate_id(conn); | ||||
947 | xcb_create_window(conn, depth, frame, rootWindow(), 0, 0, 1, 1, 0, | ||||
948 | XCB_WINDOW_CLASS_INPUT_OUTPUT, visualid, cw_mask, cw_values); | ||||
949 | m_frame.reset(frame); | ||||
950 | | ||||
951 | setWindowHandles(m_client); | ||||
952 | | ||||
953 | // Create the wrapper window | ||||
954 | xcb_window_t wrapperId = xcb_generate_id(conn); | ||||
955 | xcb_create_window(conn, depth, wrapperId, frame, 0, 0, 1, 1, 0, | ||||
956 | XCB_WINDOW_CLASS_INPUT_OUTPUT, visualid, cw_mask, cw_values); | ||||
957 | m_wrapper.reset(wrapperId); | ||||
958 | | ||||
959 | m_client.reparent(m_wrapper); | ||||
960 | | ||||
961 | // We could specify the event masks when we create the windows, but the original | ||||
962 | // Xlib code didn't. Let's preserve that behavior here for now so we don't end up | ||||
963 | // receiving any unexpected events from the wrapper creation or the reparenting. | ||||
964 | m_frame.selectInput(frame_event_mask); | ||||
965 | m_wrapper.selectInput(wrapper_event_mask); | ||||
966 | m_client.selectInput(client_event_mask); | ||||
967 | | ||||
968 | updateMouseGrab(); | ||||
969 | } | ||||
970 | | ||||
323 | void X11Client::updateInputWindow() | 971 | void X11Client::updateInputWindow() | ||
324 | { | 972 | { | ||
325 | if (!Xcb::Extensions::self()->isShapeInputAvailable()) | 973 | if (!Xcb::Extensions::self()->isShapeInputAvailable()) | ||
326 | return; | 974 | return; | ||
327 | 975 | | |||
328 | QRegion region; | 976 | QRegion region; | ||
329 | 977 | | |||
330 | if (!noBorder() && isDecorated()) { | 978 | if (!noBorder() && isDecorated()) { | ||
▲ Show 20 Lines • Show All 1969 Lines • Show Last 20 Lines |