Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include names of processes in PromptQuitDialog #2135

Merged
merged 1 commit into from
Oct 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions guake/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,20 @@ def __init__(self, parent, procs, tabs, notebooks):
else:
notebooks_str = ""

if procs == 0:
if not procs:
proc_str = _("There are no processes running")
elif procs == 1:
elif len(procs) == 1:
proc_str = _("There is a process still running")
else:
proc_str = _("There are {0} processes still running").format(procs)
proc_str = _("There are {0} processes still running").format(len(procs))

if procs:
proc_list = "\n\n" + "\n".join(f"{name} ({pid})" for pid, name in procs)
else:
proc_list = ""

self.set_markup(primary_msg)
self.format_secondary_markup(f"<b>{proc_str}{tab_str}{notebooks_str}.</b>")
self.format_secondary_markup(f"<b>{proc_str}{tab_str}{notebooks_str}.</b>{proc_list}")

def quit(self):
"""Run the "are you sure" dialog for quitting Guake"""
Expand Down
1 change: 1 addition & 0 deletions guake/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def is_run_from_git_workdir():
ALIGN_CENTER, ALIGN_LEFT, ALIGN_RIGHT = range(3)
ALIGN_TOP, ALIGN_BOTTOM = range(2)
ALWAYS_ON_PRIMARY = -1
PROMPT_NEVER, PROMPT_PROCESSES, PROMPT_ALWAYS = range(3)

# TODO this is not as fancy as as it could be
# pylint: disable=anomalous-backslash-in-string
Expand Down
10 changes: 8 additions & 2 deletions guake/guake_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
from guake.dialogs import PromptQuitDialog
from guake.globals import MAX_TRANSPARENCY
from guake.globals import NAME
from guake.globals import PROMPT_ALWAYS
from guake.globals import PROMPT_PROCESSES
from guake.globals import TABS_SESSION_SCHEMA_VERSION
from guake.gsettings import GSettingHandler
from guake.keybindings import Keybindings
Expand Down Expand Up @@ -906,13 +908,17 @@ def accel_search_terminal(self, *args):

def accel_quit(self, *args):
"""Callback to prompt the user whether to quit Guake or not."""
procs = self.notebook_manager.get_running_fg_processes_count()
procs = self.notebook_manager.get_running_fg_processes()
tabs = self.notebook_manager.get_n_pages()
notebooks = self.notebook_manager.get_n_notebooks()
prompt_cfg = self.settings.general.get_boolean("prompt-on-quit")
prompt_tab_cfg = self.settings.general.get_int("prompt-on-close-tab")
# "Prompt on tab close" config overrides "prompt on quit" config
if prompt_cfg or (prompt_tab_cfg == 1 and procs > 0) or (prompt_tab_cfg == 2):
if (
prompt_cfg
or (prompt_tab_cfg == PROMPT_PROCESSES and procs)
or (prompt_tab_cfg == PROMPT_ALWAYS)
):
log.debug("Remaining procs=%r", procs)
if PromptQuitDialog(self.window, procs, tabs, notebooks).quit():
log.info("Quitting Guake")
Expand Down
41 changes: 22 additions & 19 deletions guake/notebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@
from guake.callbacks import MenuHideCallback
from guake.callbacks import NotebookScrollCallback
from guake.dialogs import PromptQuitDialog
from guake.globals import PROMPT_ALWAYS
from guake.globals import PROMPT_PROCESSES
from guake.menus import mk_notebook_context_menu
from guake.prefs import PrefsDialog
from guake.utils import gdk_is_x11_display
from guake.utils import get_process_name
from guake.utils import save_tabs_when_changed

import gi
Expand Down Expand Up @@ -247,15 +250,15 @@ def get_terminals(self):
terminals += page.get_terminals()
return terminals

def get_running_fg_processes_count(self):
fg_proc_count = 0
def get_running_fg_processes(self):
processes = []
for page in self.iter_pages():
fg_proc_count += self.get_running_fg_processes_count_page(self.page_num(page))
return fg_proc_count
processes += self.get_running_fg_processes_page(page)
return processes

def get_running_fg_processes_count_page(self, index):
total_procs = 0
for terminal in self.get_terminals_for_page(index):
def get_running_fg_processes_page(self, page):
processes = []
for terminal in page.get_terminals():
pty = terminal.get_pty()
if not pty:
continue
Expand All @@ -265,14 +268,13 @@ def get_running_fg_processes_count_page(self, index):
fgpid = posix.tcgetpgrp(fdpty)
log.debug("found running pid: %s", fgpid)
if fgpid not in (-1, term_pid):
total_procs += 1
processes.append((fgpid, get_process_name(fgpid)))
except OSError:
log.debug(
"Cannot retrieve any pid from terminal %s, looks like it is already dead",
index,
terminal,
)
return 0
return total_procs
return processes

def has_page(self):
return self.get_n_pages() > 0
Expand All @@ -296,16 +298,17 @@ def delete_page(self, page_num, kill=True, prompt=0):
if page_num >= self.get_n_pages() or page_num < 0:
log.error("Can not delete page %s no such index", page_num)
return

page = self.get_nth_page(page_num)
# TODO NOTEBOOK it would be nice if none of the "ui" stuff
# (PromptQuitDialog) would be in here
procs = self.get_running_fg_processes_count_page(page_num)
if prompt == 2 or (prompt == 1 and procs > 0):
procs = self.get_running_fg_processes_page(page)
if prompt == PROMPT_ALWAYS or (prompt == PROMPT_PROCESSES and procs):
# TODO NOTEBOOK remove call to guake
if not PromptQuitDialog(self.guake.window, procs, -1, None).close_tab():
return

page = self.get_nth_page(page_num)
for terminal in self.get_terminals_for_page(page_num):
for terminal in page.get_terminals():
if kill:
terminal.kill()
terminal.destroy()
Expand Down Expand Up @@ -609,8 +612,8 @@ def get_n_pages(self):
def get_n_notebooks(self):
return len(self.notebooks.keys())

def get_running_fg_processes_count(self):
r_fg_c = 0
def get_running_fg_processes(self):
processes = []
for k in self.notebooks:
r_fg_c += self.notebooks[k].get_running_fg_processes_count()
return r_fg_c
processes += self.notebooks[k].get_running_fg_processes()
return processes
6 changes: 6 additions & 0 deletions guake/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
# pylint: disable=redefined-outer-name
import os

from guake.utils import FileManager
from guake.utils import get_process_name


def test_file_manager(fs):
Expand Down Expand Up @@ -37,3 +39,7 @@ def test_file_manager_clear(fs):
assert fm.read("/foo/bar") == "test"
fm.clear()
assert fm.read("/foo/bar") == "changed"


def test_process_name():
assert get_process_name(os.getpid())
14 changes: 14 additions & 0 deletions guake/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import enum
import logging
import os
import re
import subprocess
import time
import yaml
Expand Down Expand Up @@ -524,3 +525,16 @@ def draw(self, widget, cr):
cr.paint()

cr.restore()


def get_process_name(pid):
stat_file = f"/proc/{pid}/stat"
try:
with open(stat_file, "r", encoding="utf-8") as fp:
status = fp.read()
except IOError as ex:
log.debug("Unable to read %s: %s", stat_file, ex)
status = ""

match = re.match(r"\d+ \(([^)]+)\)", status)
return match.group(1) if match else None
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
release_summary: >
The "are you sure you want to close" dialog will now include names of any running processes.
features:
- |
- Include names of any processes in PromptQuitDialog, closes #256