Save it as wm.pbi:
Code: Select all
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf
DeclareModule wm
Structure window_structure
pid.i
desktop.i
client_machine$
title$
EndStructure
Declare.i open_display(Display$="")
Declare.i list_windows(*disp, List WindowList.window_structure())
Declare.i list_child_windows(*disp, win)
Declare.i get_active_window(*disp)
Declare.s get_window_title(*disp, win)
Declare.i activate_window(*disp, win)
Declare.i get_window_by_title(*disp, title$)
Declare.i window_move_resize(*disp, win, x=-1, y=-1, w=-1, h=-1)
Declare.i set_parent(*disp, w, parent, x, y)
Declare.i close_window(*disp, win)
Declare.i close_display(*disp)
EndDeclareModule
Module wm
EnableExplicit
#XA_ARC = 3
#XA_ATOM = 4
#XA_BITMAP = 5
#XA_CAP_HEIGHT = 66
#XA_CARDINAL = 6
#XA_COLORMAP = 7
#XA_COPYRIGHT = 61
#XA_CURSOR = 8
#XA_CUT_BUFFER0 = 9
#XA_CUT_BUFFER1 = 10
#XA_CUT_BUFFER2 = 11
#XA_CUT_BUFFER3 = 12
#XA_CUT_BUFFER4 = 13
#XA_CUT_BUFFER5 = 14
#XA_CUT_BUFFER6 = 15
#XA_CUT_BUFFER7 = 16
#XA_DRAWABLE = 17
#XA_END_SPACE = 46
#XA_FAMILY_NAME = 64
#XA_FONT = 18
#XA_FONT_NAME = 63
#XA_FULL_NAME = 65
#XA_INTEGER = 19
#XA_ITALIC_ANGLE = 55
#XA_LAST_PREDEFINED = 68
#XA_MAX_SPACE = 45
#XA_MIN_SPACE = 43
#XA_NORM_SPACE = 44
#XA_NOTICE = 62
#XA_PIXMAP = 20
#XA_POINT = 21
#XA_POINT_SIZE = 59
#XA_PRIMARY = 1
#XA_QUAD_WIDTH = 57
#XA_RECTANGLE = 22
#XA_RESOLUTION = 60
#XA_RESOURCE_MANAGER = 23
#XA_RGB_BEST_MAP = 25
#XA_RGB_BLUE_MAP = 26
#XA_RGB_COLOR_MAP = 24
#XA_RGB_DEFAULT_MAP = 27
#XA_RGB_GRAY_MAP = 28
#XA_RGB_GREEN_MAP = 29
#XA_RGB_RED_MAP = 30
#XA_SECONDARY = 2
#XA_STRIKEOUT_ASCENT = 53
#XA_STRIKEOUT_DESCENT = 54
#XA_STRING = 31
#XA_SUBSCRIPT_X = 49
#XA_SUBSCRIPT_Y = 50
#XA_SUPERSCRIPT_X = 47
#XA_SUPERSCRIPT_Y = 48
#XA_UNDERLINE_POSITION = 51
#XA_UNDERLINE_THICKNESS = 52
#XA_VISUALID = 32
#XA_WEIGHT = 58
#XA_WINDOW = 33
#XA_WM_CLASS = 67
#XA_WM_CLIENT_MACHINE = 36
#XA_WM_COMMAND = 34
#XA_WM_HINTS = 35
#XA_WM_ICON_NAME = 37
#XA_WM_ICON_SIZE = 38
#XA_WM_NAME = 39
#XA_WM_NORMAL_HINTS = 40
#XA_WM_SIZE_HINTS = 41
#XA_WM_TRANSIENT_FOR = 68
#XA_WM_ZOOM_HINTS = 42
#XA_X_HEIGHT = 56
#MAX_PROPERTY_VALUE_LEN = 2048
#Success = 0
#EXIT_SUCCESS = 0
#EXIT_FAILURE = 1
#ClientMessage = 33
#Expose = 12
#KeyPressMask = 1 << 0
#KeyReleaseMask = 1 << 1
#Button3MotionMask = 1 << 10
#Button4MotionMask = 1 << 11
#Button5MotionMask = 1 << 12
#ButtonMotionMask = 1 << 13
#KeymapStateMask = 1 << 14
#ExposureMask = 1 << 15
#VisibilityChangeMask = 1 << 16
#StructureNotifyMask = 1 << 17
#ResizeRedirectMask = 1 << 18
#SubstructureNotifyMask = 1 << 19
#ButtonPressMask = 1 << 2
#SubstructureRedirectMask = 1 << 20
#FocusChangeMask = 1 << 21
#PropertyChangeMask = 1 << 22
#ColormapChangeMask = 1 << 23
#ButtonReleaseMask = 1 << 3
#EnterWindowMask = 1 << 4
#LeaveWindowMask = 1 << 5
#PointerMotionMask = 1 << 6
#PointerMotionHintMask = 1 << 7
#Button1MotionMask = 1 << 8
#Button2MotionMask = 1 << 9
Structure ArrayOfInteger
i.i[0]
EndStructure
Structure XAnyEvent Align #PB_Structure_AlignC
type.i
serial.i ; # of last request processed by server
send_event.i ; true if this came from a SendEvent request
*display ; Display the event was read from
window.i
EndStructure
Structure Data_Structure Align #PB_Structure_AlignC
StructureUnion
b.a[20]
s.u[20]
i.i[5]
EndStructureUnion
EndStructure
Structure XClientMessageEvent Align #PB_Structure_AlignC
type.i ; ClientMessage
serial.i ; # of last request processed by server
send_event.i ; true if this came from a SendEvent request
*display ; Display the event was read from
window.i
message_type.i
format.i
data_.Data_Structure
EndStructure
Structure XExposeEvent Align #PB_Structure_AlignC
type.i ; Expose
serial.i ; # of last request processed by server
send_event.i ; true if this came from a SendEvent request
*display ; Display the event was read from
window.i
x.i
y.i
width.i
height.i
count.i ; if nonzero, at least this many more
EndStructure
Structure XEvent Align #PB_Structure_AlignC
StructureUnion
type.i
xany.XanyEvent
xexpose.XExposeEvent
xclient.XClientMessageEvent
pad.i[24]
EndStructureUnion
EndStructure
Macro DefaultRootWindow(disp)
XDefaultRootWindow(disp)
EndMacro
ImportC "/usr/lib/x86_64-linux-gnu/libX11.so"
XOpenDisplay.i(display.p-utf8)
XCloseDisplay.i(*display)
XInternAtom.i(*display, atom_name.p-utf8, only_if_exists)
XFree.i(*Data)
XDefaultRootWindow.i(*display)
XGetWindowProperty.i(*display, w, property, long_offset, long_length, delete, req_type, *actual_type_return, *actual_format_return, *nitems_return, *bytes_after_return, *prop_return)
XSendEvent.i(*display, w, propagate, event_mask, *event_send)
XMoveWindow.i(*display, w, x, y)
XResizeWindow.i(*display, w, width, height)
XMoveResizeWindow.i(*display, w, x, y, width, height)
XReparentWindow.i(*display, w, parent, x, y)
XUnmapWindow.i(*display, w)
XMapWindow.i(*display, w)
XMapRaised(*display, w)
XSync.i(*display, discard)
XFlush(*display)
XQueryTree.i(*display, w, *root_return, *parent_return, *children_return, *nchildren_return)
EndImport
Procedure.i client_msg(*disp, win, msg$, data0, data1, data2, data3, data4)
Protected event.XEvent, mask.i, Result.i
mask = #SubstructureRedirectMask | #SubstructureNotifyMask
event\xclient\type = #ClientMessage
event\xclient\serial = 0
event\xclient\send_event = #True
event\xclient\message_type = XInternAtom(*disp, msg$, #False)
event\xclient\window = win
event\xclient\format = 32
event\xclient\data_\i[0] = data0
event\xclient\data_\i[1] = data1
event\xclient\data_\i[2] = data2
event\xclient\data_\i[3] = data3
event\xclient\data_\i[4] = data4
If XSendEvent(*disp, DefaultRootWindow(*disp), #False, mask, @event)
XFlush(*disp)
Result = #EXIT_SUCCESS
Else
Debug "Cannot send " + msg$ + " event"
Result = #EXIT_FAILURE
EndIf
ProcedureReturn Result
EndProcedure
Procedure.i get_property(*disp, win, xa_prop_type, prop_name$, *size.Long)
Protected xa_prop_name, xa_ret_type
Protected ret_format, ret_nitems, ret_bytes_after, tmp_size
Protected *ret_prop, *ret
xa_prop_name = XInternAtom(*disp, prop_name$, #False)
; MAX_PROPERTY_VALUE_LEN / 4 explanation (XGetWindowProperty manpage):
;
; long_length = Specifies the length in 32-bit multiples of the
; Data To be retrieved.
;
If XGetWindowProperty(*disp, win, xa_prop_name, 0, #MAX_PROPERTY_VALUE_LEN / 4, #False, xa_prop_type, @xa_ret_type, @ret_format, @ret_nitems, @ret_bytes_after, @*ret_prop) = #Success
If xa_ret_type = xa_prop_type
; null terminate the result To make string handling easier (if the result is a string)
;Debug "ret_format: " + Str(ret_format) + " ret_nitems: " + Str(ret_nitems)
tmp_size = (ret_format / (32 / SizeOf(Integer))) * ret_nitems
;ShowMemoryViewer(*ret_prop, tmp_size)
*ret = AllocateMemory(tmp_size + 1)
CopyMemory(*ret_prop, *ret, tmp_size)
If *size
*size\l = tmp_size
EndIf
EndIf
XFree(*ret_prop)
EndIf
ProcedureReturn *ret
EndProcedure
Procedure.i wm_supports(*disp, prop$)
Protected.i xa_prop, size, i, Result
Protected *List.ArrayOfInteger
xa_prop = XInternAtom(*disp, prop$, #False)
*List = get_property(*disp, DefaultRootWindow(*disp), #XA_ATOM, "_NET_SUPPORTED", @size)
If *List
For i = 0 To (size / SizeOf(Integer)) - 1
If *List\i[i] = xa_prop
Result = #True
Break
EndIf
Next i
FreeMemory(*List)
Else
Debug "Cannot get _NET_SUPPORTED property."
EndIf
ProcedureReturn Result
EndProcedure
Procedure.s get_window_title(*disp, win)
Protected title_utf8$
Protected *wm_name
Protected *net_wm_name
*net_wm_name = get_property(*disp, win, XInternAtom(*disp, "UTF8_STRING", #False), "_NET_WM_NAME", #Null)
If *net_wm_name
title_utf8$ = PeekS(*net_wm_name, -1, #PB_UTF8)
FreeMemory(*net_wm_name)
Else
*wm_name = get_property(*disp, win, #XA_STRING, "WM_NAME", #Null)
If *wm_name
title_utf8$ = PeekS(*wm_name, -1, #PB_UTF8)
FreeMemory(*wm_name)
EndIf
EndIf
ProcedureReturn title_utf8$
EndProcedure
Procedure.i get_active_window(*disp)
Protected *prop
Protected size
Protected ret
*prop = get_property(*disp, DefaultRootWindow(*disp), #XA_WINDOW, "_NET_ACTIVE_WINDOW", @size)
If *prop
ret = PeekI(*prop)
FreeMemory(*prop)
EndIf
ProcedureReturn ret
EndProcedure
Procedure.i activate_window(*disp, win)
client_msg(*disp, win, "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0)
XMapRaised(*disp, win)
ProcedureReturn #True
EndProcedure
Procedure.i close_window(*disp, win)
ProcedureReturn client_msg(*disp, win, "_NET_CLOSE_WINDOW", 0, 0, 0, 0, 0)
EndProcedure
Procedure.i window_move_resize(*disp, win, x=-1, y=-1, w=-1, h=-1)
Protected.i Result, grflags
If x <> -1 : grflags | (1 << 8) : EndIf
If y <> -1 : grflags | (1 << 9) : EndIf
If w <> -1 : grflags | (1 << 10) : EndIf
If h <> -1 : grflags | (1 << 11) : EndIf
;grflags | (1 << 12) ; application issued
Debug "Win: " + Hex(win) + " grflags: " + Hex(grflags) + " x:" + Str(x) + " y:" + Str(y) + " w:" + Str(w) + " h:" + Str(h)
; If wm_supports(*disp, "_NET_MOVERESIZE_WINDOW")
; Debug "wm supports NET_MOVERESIZE_WINDOW"
; Result = client_msg(*disp, win, "_NET_MOVERESIZE_WINDOW", grflags, x, y, w, h)
; Else
If (w < 1 Or h < 1) And (x >= 0 And y >= 0)
Debug "XMoveWindow"
Result = XMoveWindow(*disp, win, x, y)
ElseIf (x < 0 Or y < 0) And (w >= 1 And h >= -1)
Debug "XResizeWindow"
Result = XResizeWindow(*disp, win, w, h)
ElseIf x >= 0 And y >= 0 And w >= 1 And h >= 1
Debug "XMoveResizeWindow"
Result = XMoveResizeWindow(*disp, win, x, y, w, h)
EndIf
; EndIf
ProcedureReturn Result
EndProcedure
Procedure.i set_parent(*disp, w, parent, x, y)
XUnmapWindow(*disp, w)
;XMapWindow(*disp, parent)
XSync(*disp, #False)
XReparentWindow(*disp, w, parent, x, y)
XMapWindow(*disp, w)
Delay(2)
XSync(*disp, #False)
EndProcedure
Procedure.i get_client_list(*disp, *size.Long)
Protected *client_list
*client_list = get_property(*disp, DefaultRootWindow(*disp), #XA_WINDOW, "_NET_CLIENT_LIST", *size)
If *client_list = #Null
*client_list = get_property(*disp, DefaultRootWindow(*disp), #XA_CARDINAL, "_WIN_CLIENT_LIST", *size)
EndIf
;Debug "*clientlist: " + Str(*client_list) + " " + Str(*size\l) + " " + PeekS(*client_list, *size\l, #PB_UTF8)
ProcedureReturn *client_list
EndProcedure
Procedure.i get_window_by_title(*disp, title$)
Protected.i i, cnt, pid
Protected *client_list.ArrayOfInteger
Protected client_list_size.i
Protected *client_machine, *desktop, *pid
*client_list = get_client_list(*disp, @client_list_size)
If *client_list
cnt = client_list_size / SizeOf(Integer) - 1
;Debug "client_list_size: " + Str(cnt + 1)
For i = 0 To cnt
If *client_list\i[i]
If get_window_title(*disp, *client_list\i[i]) = title$
pid = *client_list\i[i]
Break
EndIf
EndIf
Next i
FreeMemory(*client_list)
EndIf
ProcedureReturn pid
EndProcedure
Procedure.i list_windows(*disp, List WindowList.window_structure())
Protected *client_list.ArrayOfInteger
Protected client_list_size.i
Protected *client_machine, *desktop, *pid
Protected max_client_machine_len.i
Protected Title$
Protected i.i, cnt.i
*client_list = get_client_list(*disp, @client_list_size)
If *client_list
cnt = client_list_size / SizeOf(Integer) - 1
;Debug "client_list_size: " + Str(cnt + 1)
For i = 0 To cnt
If *client_list\i[i]
AddElement(WindowList())
WindowList()\desktop = -666
WindowList()\title$ = get_window_title(*disp, *client_list\i[i])
; client_machine
*client_machine = get_property(*disp, *client_list\i[i], #XA_STRING, "WM_CLIENT_MACHINE", #Null)
If *client_machine
WindowList()\client_machine$ = PeekS(*client_machine, -1, #PB_UTF8)
FreeMemory(*client_machine)
EndIf
; desktop ID
*desktop = get_property(*disp, *client_list\i[i], #XA_CARDINAL, "_NET_WM_DESKTOP", #Null)
If *desktop = #Null
*desktop = get_property(*disp, *client_list\i[i], #XA_CARDINAL, "_WIN_WORKSPACE", #Null)
EndIf
If *desktop
WindowList()\desktop = PeekI(*desktop)
FreeMemory(*desktop)
EndIf
; pid
*pid = get_property(*disp, *client_list\i[i], #XA_CARDINAL, "_NET_WM_PID", #Null)
If *pid
WindowList()\pid = *client_list\i[i]
FreeMemory(*pid)
EndIf
EndIf
Next i
FreeMemory(*client_list)
EndIf
ProcedureReturn ListSize(WindowList())
EndProcedure
Procedure.i list_child_windows(*disp, win)
Protected.i nchildren_return, root_return, parent_return, i
Protected *children_return.ArrayOfInteger
If XQueryTree(*disp, win, @root_return, @parent_return, @*children_return, @nchildren_return)
Debug "root_return: " + Hex(root_return)
Debug "parent_return: " + Hex(parent_return)
Debug "nchildren_return: " + Str(nchildren_return)
For i = 0 To nchildren_return - 1
Debug Hex(*children_return\i[i]) + " " + get_window_title(*disp, *children_return\i[i])
Next i
If *children_return
XFree(*children_return)
EndIf
EndIf
EndProcedure
Procedure.i open_display(Display$="")
Protected *disp
If Display$ = ""
*disp = XOpenDisplay(#Null$)
Else
*disp = XOpenDisplay(Display$)
EndIf
ProcedureReturn *disp
EndProcedure
Procedure.i close_display(*disp)
ProcedureReturn XCloseDisplay(*disp)
EndProcedure
EndModule
CompilerIf #PB_Compiler_IsMainFile
;-Demo
Define *disp, ActiveWindow.i, max_client_machine_len.i, client_machine_len
Define NewList WindowList.wm::window_structure()
If OpenConsole()
*disp = wm::open_display(#Null$)
If *disp
ActiveWindow = wm::get_active_window(*disp)
Debug "Active window: " + wm::get_window_title(*disp, ActiveWindow)
;wm::close_window(*disp, ActiveWindow) ; don't do this :)
;wm::window_move_resize(*disp, ActiveWindow, 100, 100, 800, 600)
If wm::list_windows(*disp, WindowList())
; find the longest client_machine name
ForEach WindowList()
client_machine_len = Len(WindowList()\client_machine$)
If client_machine_len > max_client_machine_len
max_client_machine_len = client_machine_len
EndIf
Next
ForEach WindowList()
If WindowList()\desktop <> -666
Debug "0x" + RSet(Hex(WindowList()\pid), 8, "0") + " " + RSet(Str(WindowList()\desktop), 2) + " " + LSet(WindowList()\client_machine$, max_client_machine_len) + " " + WindowList()\title$
PrintN("0x" + RSet(Hex(WindowList()\pid), 8, "0") + " " + RSet(Str(WindowList()\desktop), 2) + " " + LSet(WindowList()\client_machine$, max_client_machine_len) + " " + WindowList()\title$)
Else
Debug "0x" + RSet(Hex(WindowList()\pid), 8, "0") + " [x] " + LSet(WindowList()\client_machine$, max_client_machine_len) + " " + WindowList()\title$
PrintN("0x" + RSet(Hex(WindowList()\pid), 8, "0") + " [x] " + LSet(WindowList()\client_machine$, max_client_machine_len) + " " + WindowList()\title$)
EndIf
Next
EndIf
wm::close_display(*disp)
EndIf
CompilerIf #PB_Compiler_Debugger
Delay(5000)
CompilerEndIf
CloseConsole()
EndIf
CompilerEndIf