Add suspend/resume support for twin managed applications

pull/2/head
Timothy Pearson 13 years ago
parent d160a6fc01
commit 98ead41b56

@ -13,6 +13,7 @@ project( twin )
add_subdirectory( lib ) add_subdirectory( lib )
add_subdirectory( killer ) add_subdirectory( killer )
add_subdirectory( resumer )
add_subdirectory( kcmtwin ) add_subdirectory( kcmtwin )
add_subdirectory( pics ) add_subdirectory( pics )
add_subdirectory( clients ) add_subdirectory( clients )

@ -23,6 +23,7 @@ License. See the file "COPYING" for the exact licensing terms.
#include <tqwhatsthis.h> #include <tqwhatsthis.h>
#include <twin.h> #include <twin.h>
#include <kiconloader.h> #include <kiconloader.h>
#include <klocale.h>
#include <stdlib.h> #include <stdlib.h>
#include "bridge.h" #include "bridge.h"
@ -102,6 +103,7 @@ Client::Client( Workspace *ws )
in_layer( UnknownLayer ), in_layer( UnknownLayer ),
ping_timer( NULL ), ping_timer( NULL ),
process_killer( NULL ), process_killer( NULL ),
process_resumer( NULL ),
user_time( CurrentTime ), // not known yet user_time( CurrentTime ), // not known yet
allowed_actions( 0 ), allowed_actions( 0 ),
postpone_geometry_updates( 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) frame_geometry = TQRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0)
client_size = TQSize( 100, 100 ); client_size = TQSize( 100, 100 );
custom_opacity = false; custom_opacity = false;
rule_opacity_active = 0;; //translucency rules rule_opacity_active = 0; //translucency rules
rule_opacity_inactive = 0; //dito. rule_opacity_inactive = 0; //dito.
// SELI initialize xsizehints?? // SELI initialize xsizehints??
@ -678,6 +680,9 @@ void Client::minimize( bool avoid_animation )
void Client::unminimize( bool avoid_animation ) void Client::unminimize( bool avoid_animation )
{ {
if (!queryUserSuspendedResume())
return;
if( !isMinimized()) if( !isMinimized())
return; return;
@ -1840,7 +1845,7 @@ bool Client::isSuspendable() const
pid_t pid = info->pid(); pid_t pid = info->pid();
if( pid <= 0 || machine.isEmpty()) // needed properties missing if( pid <= 0 || machine.isEmpty()) // needed properties missing
return false; return false;
kdDebug( 1212 ) << "Suspend process:" << pid << "(" << machine << ")" << endl; kdDebug( 1212 ) << "Check suspendable process:" << pid << "(" << machine << ")" << endl;
if( machine != "localhost" ) if( machine != "localhost" )
{ {
return false; return false;
@ -1884,7 +1889,7 @@ bool Client::isResumeable() const
pid_t pid = info->pid(); pid_t pid = info->pid();
if( pid <= 0 || machine.isEmpty()) // needed properties missing if( pid <= 0 || machine.isEmpty()) // needed properties missing
return false; return false;
kdDebug( 1212 ) << "Suspend process:" << pid << "(" << machine << ")" << endl; kdDebug( 1212 ) << "Check resumeable process:" << pid << "(" << machine << ")" << endl;
if( machine != "localhost" ) if( machine != "localhost" )
{ {
return false; 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() void Client::suspendWindow()
{ {
TQCString machine = wmClientMachine( true ); TQCString machine = wmClientMachine( true );
@ -1934,8 +1969,27 @@ void Client::suspendWindow()
return; return;
} }
else 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 ); ::kill( pid, SIGSTOP );
} }
}
void Client::resumeWindow() void Client::resumeWindow()
{ {
@ -1949,7 +2003,26 @@ void Client::resumeWindow()
return; return;
} }
else else
{
::kill( pid, SIGCONT ); ::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() void Client::processKillerExited()
@ -1959,6 +2032,18 @@ void Client::processKillerExited()
process_killer = NULL; 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 ) void Client::setSkipTaskbar( bool b, bool from_outside )
{ {
int was_wants_tab_focus = wantsTabFocus(); int was_wants_tab_focus = wantsTabFocus();
@ -2178,7 +2263,7 @@ void Client::setCaption( const TQString& s, bool force )
info->setVisibleName( caption().utf8() ); info->setVisibleName( caption().utf8() );
reset_name = false; 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 || reset_name )) // if it was new window, it may have old value still set, if the window is reused
{ {
info->setVisibleName( "" ); // remove info->setVisibleName( "" ); // remove
@ -2270,11 +2355,13 @@ void Client::readIcons( Window win, TQPixmap* icon, TQPixmap* miniicon )
if( icon != NULL ) if( icon != NULL )
*icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints ); *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints );
if( miniicon != NULL ) if( miniicon != NULL )
{
if( icon == NULL || !icon->isNull()) if( icon == NULL || !icon->isNull())
*miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints ); *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints );
else else
*miniicon = TQPixmap(); *miniicon = TQPixmap();
} }
}
void Client::getIcons() void Client::getIcons()
{ {
@ -2745,12 +2832,14 @@ void Client::updateOpacity()
{ {
for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ ) for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ )
if ((*it)->isDialog() || (*it)->isUtility()) if ((*it)->isDialog() || (*it)->isUtility())
{
if( (*it)->ruleOpacityActive() ) if( (*it)->ruleOpacityActive() )
(*it)->setOpacity((*it)->ruleOpacityActive() < 0xFFFFFFFF, (*it)->ruleOpacityActive()); (*it)->setOpacity((*it)->ruleOpacityActive() < 0xFFFFFFFF, (*it)->ruleOpacityActive());
else else
(*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
} }
} }
}
else else
{ {
if( ruleOpacityInactive() ) if( ruleOpacityInactive() )
@ -2819,6 +2908,7 @@ void Client::updateOpacity()
{ {
for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ ) for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ )
if ((*it)->isUtility()) //don't deactivate dialogs... if ((*it)->isUtility()) //don't deactivate dialogs...
{
if( (*it)->ruleOpacityInactive() ) if( (*it)->ruleOpacityInactive() )
(*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive());
else else
@ -2826,6 +2916,7 @@ void Client::updateOpacity()
} }
} }
} }
}
void Client::updateShadowSize() void Client::updateShadowSize()
// extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows) // extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows)

@ -302,6 +302,7 @@ class Client : public TQObject, public KDecorationDefines
void killWindow(); void killWindow();
void suspendWindow(); void suspendWindow();
void resumeWindow(); void resumeWindow();
bool queryUserSuspendedResume();
void maximize( MaximizeMode ); void maximize( MaximizeMode );
void toggleShade(); void toggleShade();
void showContextHelp(); void showContextHelp();
@ -383,6 +384,7 @@ class Client : public TQObject, public KDecorationDefines
private slots: private slots:
void pingTimeout(); void pingTimeout();
void processKillerExited(); void processKillerExited();
void processResumerExited();
void demandAttentionKNotify(); void demandAttentionKNotify();
void drawShadow(); void drawShadow();
void drawShadowAfter(Client *after); void drawShadowAfter(Client *after);
@ -560,6 +562,7 @@ class Client : public TQObject, public KDecorationDefines
Layer in_layer; Layer in_layer;
TQTimer* ping_timer; TQTimer* ping_timer;
KProcess* process_killer; KProcess* process_killer;
KProcess* process_resumer;
Time ping_timestamp; Time ping_timestamp;
Time user_time; Time user_time;
unsigned long allowed_actions; unsigned long allowed_actions;
@ -598,6 +601,7 @@ class Client : public TQObject, public KDecorationDefines
TQTimer* demandAttentionKNotifyTimer; TQTimer* demandAttentionKNotifyTimer;
friend bool performTransiencyCheck(); friend bool performTransiencyCheck();
bool minimized_before_suspend;
}; };
// helper for Client::postponeGeometryUpdates() being called in pairs (true/false) // helper for Client::postponeGeometryUpdates() being called in pairs (true/false)

@ -538,6 +538,12 @@ bool Client::manage( Window w, bool isMapped )
workspace()->discardUsedWindowRules( this, false ); // remove ApplyNow rules workspace()->discardUsedWindowRules( this, false ); // remove ApplyNow rules
updateWindowRules(); // was blocked while !isManaged() 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, // 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 // but this client is not yet in Workspace's client list at this point, will
// be only done in addClient() // be only done in addClient()

@ -68,10 +68,10 @@ TQPopupMenu* Workspace::clientPopup()
advanced_popup->insertItem( i18n("Shad&ow"), Options::ShadowOp ); advanced_popup->insertItem( i18n("Shad&ow"), Options::ShadowOp );
advanced_popup->insertItem( SmallIconSet("key_bindings"), advanced_popup->insertItem( SmallIconSet("key_bindings"),
i18n("Window &Shortcut...")+'\t'+keys->shortcut("Setup Window Shortcut").seq(0).toString(), Options::SetupWindowShortcutOp ); i18n("Window &Shortcut...")+'\t'+keys->shortcut("Setup Window Shortcut").seq(0).toString(), Options::SetupWindowShortcutOp );
// FIXME advanced_popup->insertSeparator();
// 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( "suspend" ), i18n("&Suspend Application"), Options::SuspendWindowOp ); advanced_popup->insertItem( SmallIconSet( "exec" ), i18n("&Resume Application"), Options::ResumeWindowOp );
// 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 Window Settings..."), Options::WindowRulesOp );
advanced_popup->insertItem( SmallIconSet( "wizard" ), i18n("&Special Application Settings..."), Options::ApplicationRulesOp ); advanced_popup->insertItem( SmallIconSet( "wizard" ), i18n("&Special Application Settings..."), Options::ApplicationRulesOp );

@ -647,6 +647,7 @@ class Workspace : public TQObject, public KWinInterface, public KDecorationDefin
Window null_focus_window; Window null_focus_window;
bool forced_global_mouse_grab; bool forced_global_mouse_grab;
friend class StackingUpdatesBlocker; friend class StackingUpdatesBlocker;
friend class Client;
//kompmgr //kompmgr
TQSlider *transSlider; TQSlider *transSlider;

Loading…
Cancel
Save