show selected command wrapped in a separate window

For a process with a very long command, especially with many long
command line arguments, inspecting the command and its arguments could
become inconvenient.

Meanwhile htop supports the concept of "screen", or window, which is
extended here to create a dedicated "CommandScreen", making it possible
to display the command of the selected process in a separate window
meanwhile being wrapped into multiple lines.

Another benefit of using a command screen is, the user can navigate
through the wrapped lines of the command and perform actions like
searching and filtering.
This commit is contained in:
ryenus
2017-08-03 17:43:28 +08:00
committed by cgzones
parent 5233817122
commit f4bb50294a
5 changed files with 108 additions and 3 deletions

74
CommandScreen.c Normal file
View File

@ -0,0 +1,74 @@
#include "CommandScreen.h"
#include "config.h"
#include "CRT.h"
#include "IncSet.h"
#include "ListItem.h"
#include "Platform.h"
#include "StringUtils.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void CommandScreen_addLine(InfoScreen* this, char* line, const char* p, int line_offset, int len) {
memcpy(line, p - line_offset, len);
line[len] = '\0';
InfoScreen_addLine(this, line);
}
static void CommandScreen_scan(InfoScreen* this) {
Panel* panel = this->display;
int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
Panel_prune(panel);
const char* p = this->process->comm;
char* line = xMalloc(COLS + 1);
int line_offset = 0, last_spc = -1, len;
for (; *p != '\0'; p++, line_offset++) {
if (*p == ' ') last_spc = line_offset;
if (line_offset == COLS) {
len = (last_spc == -1) ? line_offset : last_spc;
CommandScreen_addLine(this, line, p, line_offset, len);
line_offset -= len;
last_spc = -1;
}
}
if (line_offset > 0) CommandScreen_addLine(this, line, p, line_offset, line_offset);
free(line);
Panel_setSelected(panel, idx);
}
static void CommandScreen_draw(InfoScreen* this) {
char* title = xMalloc(COLS + 1);
int len = snprintf(title, COLS + 1, "Command of process %d - %s", this->process->pid, this->process->comm);
if (len > COLS) {
memset(&title[COLS - 3], '.', 3);
}
InfoScreen_drawTitled(this, "%s", title);
free(title);
}
InfoScreenClass CommandScreen_class = {
.super = {
.extends = Class(Object),
.delete = CommandScreen_delete
},
.scan = CommandScreen_scan,
.draw = CommandScreen_draw
};
CommandScreen* CommandScreen_new(Process* process) {
CommandScreen* this = AllocThis(CommandScreen);
return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " ");
}
void CommandScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this));
}