-
-
Notifications
You must be signed in to change notification settings - Fork 174
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#2539 add window info to header menu
git-svn-id: https://xpra.org/svn/Xpra/trunk@26322 3bb7dfac-3a0b-4e04-842a-767bc560f471
- Loading branch information
Showing
2 changed files
with
261 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
# -*- coding: utf-8 -*- | ||
# This file is part of Xpra. | ||
# Copyright (C) 2020 Antoine Martin <[email protected]> | ||
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any | ||
# later version. See the file COPYING for details. | ||
|
||
from gi.repository import Gtk, GLib | ||
|
||
from xpra.util import typedict, csv, WORKSPACE_UNSET | ||
from xpra.os_util import bytestostr | ||
from xpra.client import mixin_features | ||
from xpra.common import GRAVITY_STR | ||
from xpra.gtk_common.gtk_util import ( | ||
add_close_accel, label, | ||
TableBuilder, imagebutton, get_gtk_version_info, | ||
) | ||
from xpra.log import Logger | ||
|
||
log = Logger("info") | ||
|
||
|
||
def slabel(text="", tooltip=None, font=None): | ||
l = label(text, tooltip, font) | ||
l.set_selectable(True) | ||
return l | ||
|
||
|
||
def x(self): | ||
self.size_constraints = typedict() | ||
self.geometry_hints = {} | ||
self.pending_refresh = [] | ||
|
||
|
||
class WindowInfo(Gtk.Window): | ||
|
||
def __init__(self, client, window): | ||
Gtk.Window.__init__(self) | ||
add_close_accel(self, self.destroy) | ||
self._client = client | ||
self._window = window | ||
self.is_closed = False | ||
self.set_title("Window Information for %s" % window.get_title()) | ||
self.set_destroy_with_parent(True) | ||
self.set_resizable(True) | ||
self.set_decorated(True) | ||
self.set_size_request(480, 640) | ||
self.set_transient_for(window) | ||
self.set_icon(client.get_pixbuf("information.png")) | ||
self.set_position(Gtk.WindowPosition.CENTER_ON_PARENT) | ||
def window_deleted(*_args): | ||
self.is_closed = True | ||
self.connect('delete_event', window_deleted) | ||
|
||
tb = TableBuilder(1, 2) | ||
self.wid_label = slabel() | ||
tb.new_row("Window ID", self.wid_label) | ||
self.title_label = slabel() | ||
self.title_label.set_line_wrap(True) | ||
self.title_label.set_size_request(400, -1) | ||
tb.new_row("Title", self.title_label) | ||
self.or_image = Gtk.Image() | ||
tb.new_row("Override-Redirect", self.or_image) | ||
self.state_label = slabel() | ||
tb.new_row("State", self.state_label) | ||
self.attributes_label = slabel() | ||
tb.new_row("Attributes", self.attributes_label) | ||
self.focus_image = Gtk.Image() | ||
tb.new_row("Focus", self.focus_image) | ||
self.button_state_label = slabel() | ||
tb.new_row("Button State", self.button_state_label) | ||
#self.group_leader_label = slabel() | ||
#tb.new_row("Group Leader", self.group_leader_label) | ||
tb.new_row("", slabel()) | ||
self.gravity_label = slabel() | ||
tb.new_row("Gravity", self.gravity_label) | ||
self.content_type_label = slabel() | ||
tb.new_row("Content Type", self.content_type_label) | ||
tb.new_row("", slabel()) | ||
self.pixel_depth_label = slabel() | ||
tb.new_row("Pixel Depth", self.pixel_depth_label) | ||
self.alpha_image = Gtk.Image() | ||
tb.new_row("Alpha Channel", self.alpha_image) | ||
self.opengl_image = Gtk.Image() | ||
tb.new_row("OpenGL", self.opengl_image) | ||
tb.new_row("", slabel()) | ||
self.geometry_label = slabel() | ||
tb.new_row("Geometry", self.geometry_label) | ||
self.outer_geometry_label = slabel() | ||
tb.new_row("Outer Geometry", self.outer_geometry_label) | ||
self.inner_geometry_label = slabel() | ||
tb.new_row("Inner Geometry", self.inner_geometry_label) | ||
self.offsets_label = slabel() | ||
tb.new_row("Offsets", self.offsets_label) | ||
self.frame_extents_label = slabel() | ||
tb.new_row("Frame Extents", self.frame_extents_label) | ||
self.max_size_label = slabel() | ||
tb.new_row("Maximum Size", self.max_size_label) | ||
self.size_constraints_label = slabel() | ||
tb.new_row("Size Constraints", self.size_constraints_label) | ||
self.add(tb.get_table()) | ||
|
||
def destroy(self, *_args): | ||
self.is_closed = True | ||
Gtk.Window.destroy(self) | ||
|
||
def show(self): | ||
self.populate() | ||
super().show_all() | ||
GLib.timeout_add(1000, self.populate) | ||
|
||
def populate(self): | ||
if self.is_closed: | ||
return False | ||
self.do_populate() | ||
return True | ||
|
||
def do_populate(self): | ||
w = self._window | ||
if not w: | ||
return | ||
def dict_str(d): | ||
return "\n".join("%s : %s" % (k,v) for k,v in d.items()) | ||
def get_window_state(): | ||
state = [] | ||
for s in ("fullscreen", "maximized", | ||
"above", "below", "shaded", "sticky", | ||
"skip-pager", "skip-taskbar", | ||
"iconified"): | ||
#ie: "skip-pager" -> self.window._skip_pager | ||
if getattr(w, "_%s" % s.replace("-", "_"), False): | ||
state.append(s) | ||
for s in ("modal", ): | ||
fn = getattr(w, "get_%s" % s, None) | ||
if fn and fn(): | ||
state.append(s) | ||
return csv(state) or "none" | ||
def get_attributes(): | ||
attr = {} | ||
workspace = w.get_desktop_workspace() | ||
if workspace>0 and workspace!=WORKSPACE_UNSET: | ||
attr["workspace"] = workspace | ||
opacity = w.get_opacity() | ||
if opacity<1: | ||
attr["opacity"] = opacity | ||
role = w.get_role() | ||
if role: | ||
attr["role"] = role | ||
#get_type_hint | ||
return dict_str(attr) | ||
self.wid_label.set_text(str(w._id)) | ||
self.title_label.set_text(w.get_title()) | ||
self.bool_icon(self.or_image, w._override_redirect) | ||
self.state_label.set_text(get_window_state()) | ||
self.attributes_label.set_text(get_attributes()) | ||
self.bool_icon(self.focus_image, w._focused) | ||
self.button_state_label.set_text(csv(b for b,s in w.button_state.items() if s) or "none") | ||
#self.group_leader_label.set_text(str(w.group_leader)) | ||
self.gravity_label.set_text(GRAVITY_STR.get(w.window_gravity, "invalid")) | ||
self.content_type_label.set_text(w.content_type or "unknown") | ||
#geometry: | ||
self.pixel_depth_label.set_text(str(w.pixel_depth or 24)) | ||
self.bool_icon(self.alpha_image, w._window_alpha) | ||
self.bool_icon(self.opengl_image, getattr(w._backing, "gl_setup", False)) | ||
#tells us if this window instance can paint with alpha | ||
self._window_alpha = False | ||
def geom_str(geom): | ||
return "%ix%i at %i,%i" % (geom[2], geom[3], geom[0], geom[1]) | ||
geom = list(w._pos)+list(w._size) | ||
self.geometry_label.set_text(geom_str(geom)) | ||
geom = list(w.get_position()) + list(w.get_size()) | ||
self.outer_geometry_label.set_text(geom_str(geom)) | ||
self.inner_geometry_label.set_text(geom_str(w.get_drawing_area_geometry())) | ||
self.offsets_label.set_text(",".join(w.window_offset or []) or "none") | ||
self.frame_extents_label.set_text(csv(w._current_frame_extents or []) or "none") | ||
self.max_size_label.set_text(csv(w.max_window_size)) | ||
def hsc(sc): | ||
#make the dict more human readable | ||
ssc = dict((bytestostr(k),v) for k,v in sc.items()) | ||
ssc.pop("gravity", None) | ||
return dict_str(ssc) | ||
self.size_constraints_label.set_text(hsc(w.size_constraints)) | ||
|
||
def bool_icon(self, image, on_off): | ||
c = self._client | ||
if not c: | ||
return | ||
if on_off: | ||
icon = c.get_pixbuf("ticked-small.png") | ||
else: | ||
icon = c.get_pixbuf("unticked-small.png") | ||
image.set_from_pixbuf(icon) | ||
|
||
def show_opengl_state(self): | ||
if self.client.opengl_enabled: | ||
glinfo = "%s / %s" % ( | ||
self.client.opengl_props.get("vendor", ""), | ||
self.client.opengl_props.get("renderer", ""), | ||
) | ||
display_mode = self.client.opengl_props.get("display_mode", []) | ||
bit_depth = self.client.opengl_props.get("depth", 0) | ||
info = [] | ||
if bit_depth: | ||
info.append("%i-bit" % bit_depth) | ||
if "DOUBLE" in display_mode: | ||
info.append("double buffering") | ||
elif "SINGLE" in display_mode: | ||
info.append("single buffering") | ||
else: | ||
info.append("unknown buffering") | ||
if "ALPHA" in display_mode: | ||
info.append("with transparency") | ||
else: | ||
info.append("without transparency") | ||
else: | ||
#info could be telling us that the gl bindings are missing: | ||
glinfo = self.client.opengl_props.get("info", "disabled") | ||
info = ["n/a"] | ||
self.client_opengl_label.set_text(glinfo) | ||
self.opengl_buffering.set_text(" ".join(info)) | ||
|
||
def show_window_renderers(self): | ||
if not mixin_features.windows: | ||
return | ||
wr = [] | ||
renderers = {} | ||
for wid, window in tuple(self.client._id_to_window.items()): | ||
renderers.setdefault(window.get_backing_class(), []).append(wid) | ||
for bclass, windows in renderers.items(): | ||
wr.append("%s (%i)" % (bclass.__name__.replace("Backing", ""), len(windows))) | ||
self.window_rendering.set_text("GTK3: %s" % csv(wr)) | ||
|
||
self.bool_icon(self.client_opengl_icon, self.client.client_supports_opengl) | ||
|
||
def get_window_encoder_stats(self): | ||
window_encoder_stats = {} | ||
#new-style server with namespace (easier): | ||
window_dict = self.client.server_last_info.get("window") | ||
if window_dict and isinstance(window_dict, dict): | ||
for k,v in window_dict.items(): | ||
try: | ||
wid = int(k) | ||
encoder_stats = v.get("encoder") | ||
if encoder_stats: | ||
window_encoder_stats[wid] = encoder_stats | ||
except Exception: | ||
log.error("Error: cannot lookup window dict", exc_info=True) | ||
return window_encoder_stats |