OSProcessMonitor.st
author Claus Gittinger <cg@exept.de>
Mon, 20 Jan 2020 21:02:47 +0100
changeset 19422 c6ca1c3e0fd7
parent 19345 1958e49daaea
permissions -rw-r--r--
#REFACTORING by exept class: MultiViewToolApplication added: #askForFile:default:forSave:thenDo: changed: #askForFile:default:thenDo: #askForFile:thenDo: #menuSaveAllAs #menuSaveAs

"
 COPYRIGHT (c) 2019 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libtool' }"

"{ NameSpace: Smalltalk }"

SystemStatusMonitor subclass:#OSProcessMonitor
	instanceVariableNames:'processes showTime filterLiveProcessesOnlyHolder'
	classVariableNames:''
	poolDictionaries:''
	category:'Monitors-ST/X'
!

!OSProcessMonitor class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2019 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    This view shows OS processes started by smalltalk - a debugging tool.

    [disclaimer:]
        this is based on one of the oldest tools in the system, written in the early 90's.
        It does in no way reflect the way GUIs are designed/written these days.
        However, after all those years, they are still very very useful (and not found in many other systems)  
        
    [see also:]
        OSProcess

    [author:]
        Claus Gittinger

    [start with:]
        OSProcessMonitor open
"
! !

!OSProcessMonitor class methodsFor:'defaults'!

defaultLabel
    ^ 'OS Process Monitor'

    "Created: 23.1.1997 / 02:52:53 / cg"
! !

!OSProcessMonitor class methodsFor:'menu specs'!

filterMenu
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."


    "
     MenuEditor new openOnClass:ExternalStreamMonitor andSelector:#filterMenu
     (Menu new fromLiteralArrayEncoding:(ExternalStreamMonitor filterMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'Live Processes Only'
            indication: filterLiveProcessesOnlyHolder
          )
         )
        nil
        nil
      )
!

mainMenu
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."


    "
     MenuEditor new openOnClass:OSProcessMonitor andSelector:#mainMenu
     (Menu new fromLiteralArrayEncoding:(OSProcessMonitor mainMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu 
       (MenuItem
          label: 'File'
          submenuChannel: fileMenu
       ) 
       (MenuItem
          label: 'View'
          submenuChannel: viewMenu
       ) 
         (MenuItem
            label: 'Filter'
            submenuChannel: filterMenu
          )
       (MenuItem
          label: 'Operations'
          submenuChannel: operationsMenu
       )
     )
!

operationsMenu
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."


    "
     MenuEditor new openOnClass:ExternalStreamMonitor andSelector:#operationsMenu
     (Menu new fromLiteralArrayEncoding:(ExternalStreamMonitor operationsMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'Terminate'
            itemValue: menuTerminateSelected
            enabled: hasSelection
          )
         (MenuItem
            label: 'Terminate Group'
            itemValue: menuTerminateSelectedGroup
            enabled: hasSelection
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'Inspect'
            itemValue: inspectProcess
            enabled: hasSelection
          )
         (MenuItem
            label: 'Owners'
            itemValue: inspectOwners
            enabled: hasSelection
          )
         )
        nil
        nil
      )
! !

!OSProcessMonitor methodsFor:'aspects'!

filterLiveProcessesOnlyHolder
    filterLiveProcessesOnlyHolder isNil ifTrue:[
        filterLiveProcessesOnlyHolder := true asValue.
        filterLiveProcessesOnlyHolder onChangeSend:#filterHolderChanged to:self.
    ].    
    ^ filterLiveProcessesOnlyHolder
! !

!OSProcessMonitor methodsFor:'drawing'!

titleLine
    "/ 123456789|12345|1234567890
    ^ 'PID    Status  Command                                              '.
    
    "
     OSProcessMonitor open
    "

    "Modified: / 17-06-1998 / 14:52:48 / cg"
    "Modified: / 29-10-2018 / 15:28:22 / Claus Gittinger"
!

updateList
    "update list of processes"

    |newList|

    shown ifTrue:[
        newList := OSProcess allSubInstances select:[:p | p pid notNil].
        self filterLiveProcessesOnlyHolder value ifTrue:[
            newList := newList select:[:p | p isAlive].
        ].
        newList sort:[:a :b | a numericPid < b numericPid].

        newList ~= processes ifTrue:[
            self updateStatus:newList
        ].
    ].
    self installDelayedUpdate.

    "Modified: / 29-10-2018 / 15:39:43 / Claus Gittinger"
!

updateStatus:newList
    "update status display of processes"

    |numberOfHeadlines oldList list oldSelection newSelection|

    shown ifTrue:[
        numberOfHeadlines := self numberOfHeadlines.

        oldList := listView list.
        oldSelection := listView selection.
        oldSelection notNil ifTrue:[
            oldSelection := oldSelection 
                                select:[:lineNr | lineNr > numberOfHeadlines]
                                thenCollect:[:lineNr | 
                                    processes at:(lineNr - numberOfHeadlines) ifAbsent:nil 
                                ].
            newSelection := OrderedCollection new.
        ].

        newList notNil ifTrue:[
            processes := WeakArray withAll:newList.
        ].
        processes notNil ifTrue:[
            list := OrderedCollection new:(processes size + numberOfHeadlines).
            showTime == true ifTrue:[
                list add:'Time: ',(Time now printString).
            ].
            list add:self titleLine.
            list add:(String new:(self titleLine size+20) withAll:$-).

            processes validElementsDo:[:process |
                |waiters waitersNames type globalNameOrNil handleString
                 isOpen handle detail color line status exitCode| 

                (status := process exitStatus) notNil ifTrue:[
                    exitCode := status code.
                ].

                line := (process pid printString) paddedTo:9.
                line := line , ' '.
                line := line , ((exitCode isNil ifTrue:[''] ifFalse:[exitCode printString]) paddedTo:4).
                line := line , ' '.
                line := line , (process commandString).

                process isAlive ifFalse:[
                    status success ifTrue:[
                        color := Color grey.
                    ] ifFalse:[
                        color := Color red.
                    ].
                    line := line withColor:color.
                ].

                list add:line.
                oldSelection notNil ifTrue:[
                    (oldSelection includesIdentical:process) ifTrue:[
                        newSelection add:list size.
                    ]
                ]
            ].
        ].
        "avoid flicker"
        (oldList notNil and:[oldList size == list size]) ifTrue:[
            list keysAndValuesDo:[:idx :entry |
                (oldList at:idx) ~= entry ifTrue:[
                    listView at:idx put:entry
                ]
            ]
        ] ifFalse:[
            listView setList:list.
            "the first few entries cannot be selected"
            1 to:numberOfHeadlines do:[:lNr |
                listView attributeAt:numberOfHeadlines put:#disabled.
            ].
        ].
        newSelection notNil ifTrue:[
            listView selectWithoutScroll:newSelection
        ].
        listView flush
    ].
    self installDelayedUpdate.

    "Modified: / 11-10-2017 / 13:56:14 / cg"
    "Modified: / 07-06-2019 / 22:10:36 / Claus Gittinger"
! !

!OSProcessMonitor methodsFor:'menu'!

selectedProcesses
    "return all selected streams"

    ^ Array streamContents:[:s |
        self selectedProcessesDo:[:each | s nextPut:each]
    ].

    "Created: / 29-10-2018 / 17:00:35 / Claus Gittinger"
!

selectedProcessesDo:aBlock
    "evaluate aBlock on all selected streams"

    self selectionIndicesDo:[:n |
        |nr process|
        
        nr := n - self numberOfHeadlines.
        nr notNil ifTrue:[
            nr > 0 ifTrue:[
                process := processes at:nr.
                (process notNil and:[process ~~ 0]) ifTrue:[
                   aBlock value:process
                ]
            ]
        ]
    ].
!

statusMenu
    "return a popUpMenu"

    <resource: #programMenu>

    |labels selectors m|

    labels := resources array:#(
                         'Terminate'
                         'Terminate Group'
                         '-'
                         'Inspect'
                         'References'
                        ).
    selectors := #(
                         menuTerminateSelected
                         menuTerminateSelectedGroup
                         nil
                         inspectProcess
                         menuReferences
                        ).

    updateProcess isNil ifTrue:[
        labels := labels,(resources array:#('-' 'Update' )).
        selectors := selectors , #(nil updateView). 
    ].

    m := PopUpMenu labels:labels
                   selectors:selectors.


    listView hasSelection ifFalse:[
        m disableAll:#(
                         menuCloseSelected
                         inspectStream
                      )
    ].
    ^ m

    "Modified: / 18-08-2017 / 14:34:12 / cg"
! !

!OSProcessMonitor methodsFor:'queries'!

numberOfHeadlines
    ^ 2 + (showTime == true ifTrue:1 ifFalse:0)

    "Modified: / 29-10-2018 / 15:24:14 / Claus Gittinger"
! !

!OSProcessMonitor methodsFor:'user actions'!

doubleClicked
    "open an inspector on the selected process"

    self inspectProcess

    "Created: / 23-01-1997 / 03:22:04 / cg"
    "Modified: / 31-01-1997 / 22:33:27 / cg"
    "Modified (comment): / 07-04-2017 / 14:28:01 / cg"
!

filterHolderChanged
    self sensor pushUserEvent:#updateList for:self
!

inspectProcess
    "open an inspector on the selected process(es)"

    self selectedProcessesDo:[:eachProcess | eachProcess inspect]

    "Modified (format): / 07-04-2017 / 14:27:45 / cg"
!

menuReferences
    "show references to the selected processes"

    ObjectMemory 
        displayRefChainToAny:(self selectedProcesses) 
        limitNumberOfSearchedReferences:100.

    "Modified (format): / 07-04-2017 / 14:27:45 / cg"
    "Modified: / 29-10-2018 / 17:01:00 / Claus Gittinger"
!

menuTerminateSelected
    "close the selected stream(s)"

    self selectedProcessesDo:[:eachProcess | eachProcess terminate].
    self updateList.

    "Created: / 07-04-2017 / 14:26:04 / cg"
!

menuTerminateSelectedGroup
    "close the selected stream(s)"

    self selectedProcessesDo:[:eachProcess | eachProcess terminateGroup].
    self updateList.

    "Created: / 07-04-2017 / 14:26:04 / cg"
! !

!OSProcessMonitor class methodsFor:'documentation'!

version_CVS
    ^ '$Header$'
! !