From 98ead41b56d43b01c980a017949d351581769d29 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Mon, 20 Feb 2012 13:57:39 -0600 Subject: Add suspend/resume support for twin managed applications --- twin/CMakeLists.txt | 1 + twin/client.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++++++--- twin/client.h | 4 +++ twin/manage.cpp | 6 ++++ twin/useractions.cpp | 8 ++--- twin/workspace.h | 1 + 6 files changed, 111 insertions(+), 8 deletions(-) (limited to 'twin') diff --git a/twin/CMakeLists.txt b/twin/CMakeLists.txt index cfdcf5e18..aa9f41057 100644 --- a/twin/CMakeLists.txt +++ b/twin/CMakeLists.txt @@ -13,6 +13,7 @@ project( twin ) add_subdirectory( lib ) add_subdirectory( killer ) +add_subdirectory( resumer ) add_subdirectory( kcmtwin ) add_subdirectory( pics ) add_subdirectory( clients ) diff --git a/twin/client.cpp b/twin/client.cpp index ff890404e..56b92e699 100644 --- a/twin/client.cpp +++ b/twin/client.cpp @@ -23,6 +23,7 @@ License. See the file "COPYING" for the exact licensing terms. #include #include #include +#include #include #include "bridge.h" @@ -102,6 +103,7 @@ Client::Client( Workspace *ws ) in_layer( UnknownLayer ), ping_timer( NULL ), process_killer( NULL ), + process_resumer( NULL ), user_time( CurrentTime ), // not known yet allowed_actions( 0 ), postpone_geometry_updates( 0 ), @@ -174,7 +176,7 @@ Client::Client( Workspace *ws ) frame_geometry = TQRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) client_size = TQSize( 100, 100 ); custom_opacity = false; - rule_opacity_active = 0;; //translucency rules + rule_opacity_active = 0; //translucency rules rule_opacity_inactive = 0; //dito. // SELI initialize xsizehints?? @@ -678,6 +680,9 @@ void Client::minimize( bool avoid_animation ) void Client::unminimize( bool avoid_animation ) { + if (!queryUserSuspendedResume()) + return; + if( !isMinimized()) return; @@ -1840,7 +1845,7 @@ bool Client::isSuspendable() const pid_t pid = info->pid(); if( pid <= 0 || machine.isEmpty()) // needed properties missing return false; - kdDebug( 1212 ) << "Suspend process:" << pid << "(" << machine << ")" << endl; + kdDebug( 1212 ) << "Check suspendable process:" << pid << "(" << machine << ")" << endl; if( machine != "localhost" ) { return false; @@ -1884,7 +1889,7 @@ bool Client::isResumeable() const pid_t pid = info->pid(); if( pid <= 0 || machine.isEmpty()) // needed properties missing return false; - kdDebug( 1212 ) << "Suspend process:" << pid << "(" << machine << ")" << endl; + kdDebug( 1212 ) << "Check resumeable process:" << pid << "(" << machine << ")" << endl; if( machine != "localhost" ) { return false; @@ -1922,6 +1927,36 @@ bool Client::isResumeable() const } } +bool Client::queryUserSuspendedResume() + { + if (isResumeable()) + { + if (process_resumer != NULL) + { + return false; + } + process_resumer = new KProcess( this ); + *process_resumer << KStandardDirs::findExe( "twin_resumer_helper" ) + << "--pid" << TQCString().setNum( info->pid() ) << "--hostname" << wmClientMachine( true ) + << "--windowname" << caption().utf8() + << "--applicationname" << resourceClass() + << "--wid" << TQCString().setNum( window()); + connect( process_resumer, TQT_SIGNAL( processExited( KProcess* )), + TQT_SLOT( processResumerExited())); + if( !process_resumer->start( KProcess::NotifyOnExit )) + { + delete process_resumer; + process_resumer = NULL; + return true; + } + return false; + } + else + { + return true; + } + } + void Client::suspendWindow() { TQCString machine = wmClientMachine( true ); @@ -1934,7 +1969,26 @@ void Client::suspendWindow() return; } else + { + for ( ClientList::ConstIterator it = workspace()->clients.begin(); it != workspace()->clients.end(); ++it) + { + Client* nextclient = *it; + pid_t nextpid = nextclient->info->pid(); + TQCString nextmachine = nextclient->wmClientMachine( true ); + if( nextpid > 0 && (!nextmachine.isEmpty())) + { + if( ( nextmachine == "localhost" ) && ( pid == nextpid ) ) + { + TQString newCaption = TQString(readName()).append(" <").append(i18n("Suspended")).append(">"); + nextclient->info->setVisibleName(newCaption.utf8()); + nextclient->info->setVisibleIconName(newCaption.utf8()); + nextclient->minimized_before_suspend = nextclient->isMinimized(); + nextclient->minimize(true); + } + } + } ::kill( pid, SIGSTOP ); + } } void Client::resumeWindow() @@ -1949,7 +2003,26 @@ void Client::resumeWindow() return; } else + { ::kill( pid, SIGCONT ); + for ( ClientList::ConstIterator it = workspace()->clients.begin(); it != workspace()->clients.end(); ++it) + { + Client* nextclient = *it; + pid_t nextpid = nextclient->info->pid(); + TQCString nextmachine = nextclient->wmClientMachine( true ); + if( nextpid > 0 && (!nextmachine.isEmpty())) + { + if( ( nextmachine == "localhost" ) && ( pid == nextpid ) ) + { + if (!nextclient->minimized_before_suspend) + { + nextclient->unminimize(true); + } + nextclient->updateCaption(); + } + } + } + } } void Client::processKillerExited() @@ -1959,6 +2032,18 @@ void Client::processKillerExited() process_killer = NULL; } +void Client::processResumerExited() + { + kdDebug( 1212 ) << "Resumer exited" << endl; + if (process_resumer->exitStatus() == 0) + { + resumeWindow(); + takeFocus( Allowed ); + } + delete process_resumer; + process_resumer = NULL; + } + void Client::setSkipTaskbar( bool b, bool from_outside ) { int was_wants_tab_focus = wantsTabFocus(); @@ -2178,7 +2263,7 @@ void Client::setCaption( const TQString& s, bool force ) info->setVisibleName( caption().utf8() ); reset_name = false; } - if(( was_suffix && cap_suffix.isEmpty() + if(( (was_suffix && cap_suffix.isEmpty()) || reset_name )) // if it was new window, it may have old value still set, if the window is reused { info->setVisibleName( "" ); // remove @@ -2270,10 +2355,12 @@ void Client::readIcons( Window win, TQPixmap* icon, TQPixmap* miniicon ) if( icon != NULL ) *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints ); if( miniicon != NULL ) + { if( icon == NULL || !icon->isNull()) *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints ); else *miniicon = TQPixmap(); + } } void Client::getIcons() @@ -2745,10 +2832,12 @@ void Client::updateOpacity() { for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ ) if ((*it)->isDialog() || (*it)->isUtility()) + { if( (*it)->ruleOpacityActive() ) (*it)->setOpacity((*it)->ruleOpacityActive() < 0xFFFFFFFF, (*it)->ruleOpacityActive()); else (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); + } } } else @@ -2819,10 +2908,12 @@ void Client::updateOpacity() { for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ ) if ((*it)->isUtility()) //don't deactivate dialogs... + { if( (*it)->ruleOpacityInactive() ) (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); else (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity); + } } } } diff --git a/twin/client.h b/twin/client.h index 813431065..c5365b578 100644 --- a/twin/client.h +++ b/twin/client.h @@ -302,6 +302,7 @@ class Client : public TQObject, public KDecorationDefines void killWindow(); void suspendWindow(); void resumeWindow(); + bool queryUserSuspendedResume(); void maximize( MaximizeMode ); void toggleShade(); void showContextHelp(); @@ -383,6 +384,7 @@ class Client : public TQObject, public KDecorationDefines private slots: void pingTimeout(); void processKillerExited(); + void processResumerExited(); void demandAttentionKNotify(); void drawShadow(); void drawShadowAfter(Client *after); @@ -560,6 +562,7 @@ class Client : public TQObject, public KDecorationDefines Layer in_layer; TQTimer* ping_timer; KProcess* process_killer; + KProcess* process_resumer; Time ping_timestamp; Time user_time; unsigned long allowed_actions; @@ -598,6 +601,7 @@ class Client : public TQObject, public KDecorationDefines TQTimer* demandAttentionKNotifyTimer; friend bool performTransiencyCheck(); + bool minimized_before_suspend; }; // helper for Client::postponeGeometryUpdates() being called in pairs (true/false) diff --git a/twin/manage.cpp b/twin/manage.cpp index 2c6777153..acf1ce598 100644 --- a/twin/manage.cpp +++ b/twin/manage.cpp @@ -538,6 +538,12 @@ bool Client::manage( Window w, bool isMapped ) workspace()->discardUsedWindowRules( this, false ); // remove ApplyNow rules updateWindowRules(); // was blocked while !isManaged() +// Handle suspended processes + if (isResumeable()) + { + suspendWindow(); // It won't hurt to stop the process again, and this will update the displayed captions + } + // TODO there's a small problem here - isManaged() depends on the mapping state, // but this client is not yet in Workspace's client list at this point, will // be only done in addClient() diff --git a/twin/useractions.cpp b/twin/useractions.cpp index 6542cdb99..e036adc76 100644 --- a/twin/useractions.cpp +++ b/twin/useractions.cpp @@ -68,10 +68,10 @@ TQPopupMenu* Workspace::clientPopup() advanced_popup->insertItem( i18n("Shad&ow"), Options::ShadowOp ); advanced_popup->insertItem( SmallIconSet("key_bindings"), i18n("Window &Shortcut...")+'\t'+keys->shortcut("Setup Window Shortcut").seq(0).toString(), Options::SetupWindowShortcutOp ); -// FIXME -// Uncomment these actions when twin can handle suspended applications in a user friendly manner -// advanced_popup->insertItem( SmallIconSet( "suspend" ), i18n("&Suspend Application"), Options::SuspendWindowOp ); -// advanced_popup->insertItem( SmallIconSet( "exec" ), i18n("&Resume Application"), Options::ResumeWindowOp ); + advanced_popup->insertSeparator(); + advanced_popup->insertItem( SmallIconSet( "suspend" ), i18n("&Suspend Application"), Options::SuspendWindowOp ); + advanced_popup->insertItem( SmallIconSet( "exec" ), i18n("&Resume Application"), Options::ResumeWindowOp ); + advanced_popup->insertSeparator(); advanced_popup->insertItem( SmallIconSet( "wizard" ), i18n("&Special Window Settings..."), Options::WindowRulesOp ); advanced_popup->insertItem( SmallIconSet( "wizard" ), i18n("&Special Application Settings..."), Options::ApplicationRulesOp ); diff --git a/twin/workspace.h b/twin/workspace.h index 6a161c52e..fb9302a00 100644 --- a/twin/workspace.h +++ b/twin/workspace.h @@ -647,6 +647,7 @@ class Workspace : public TQObject, public KWinInterface, public KDecorationDefin Window null_focus_window; bool forced_global_mouse_grab; friend class StackingUpdatesBlocker; + friend class Client; //kompmgr TQSlider *transSlider; -- cgit v1.2.3