iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
Software Development

用python做一個X11 WM系列 第 17 篇

Day 17|優先用WM_DELETE_WINDOW關閉視窗;把聚焦視窗放在最上層

  • 分享至 

  • xImage
  •  
from Xlib import display,X,XK,xobject,Xcursorfont
from Xlib.protocol import event as xevent
dpy=display.Display()
screen=dpy.screen()
root=screen.root
q_code=dpy.keysym_to_keycode(XK.string_to_keysym("q"))
one_code=dpy.keysym_to_keycode(XK.string_to_keysym("1"))
two_code=dpy.keysym_to_keycode(XK.string_to_keysym("2"))
three_code=dpy.keysym_to_keycode(XK.string_to_keysym("3"))
four_code=dpy.keysym_to_keycode(XK.string_to_keysym("4"))
five_code=dpy.keysym_to_keycode(XK.string_to_keysym("5"))
six_code=dpy.keysym_to_keycode(XK.string_to_keysym("6"))
seven_code=dpy.keysym_to_keycode(XK.string_to_keysym("7"))
eight_code=dpy.keysym_to_keycode(XK.string_to_keysym("8"))
nine_code=dpy.keysym_to_keycode(XK.string_to_keysym("9"))
ten_code=dpy.keysym_to_keycode(XK.string_to_keysym("0"))
#註冊事件
screen.root.grab_key(q_code,X.Mod4Mask|X.Mod2Mask|X.LockMask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(q_code,X.Mod4Mask|X.LockMask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(q_code,X.Mod4Mask|X.Mod2Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(q_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)

screen.root.grab_key(one_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(two_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(three_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(four_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(five_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(six_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(seven_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(eight_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(nine_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_key(ten_code,X.Mod4Mask,1,X.GrabModeAsync, X.GrabModeAsync)
screen.root.grab_button(X.Button1,X.Mod4Mask|X.Mod2Mask|X.LockMask,True,X.ButtonPressMask| X.ButtonReleaseMask| X.PointerMotionMask,X.GrabModeAsync,X.GrabModeAsync,X.NONE,X.NONE,)
screen.root.grab_button(X.Button1,X.Mod4Mask|X.LockMask,True,X.ButtonPressMask| X.ButtonReleaseMask| X.PointerMotionMask,X.GrabModeAsync,X.GrabModeAsync,X.NONE,X.NONE,)
screen.root.grab_button(X.Button1,X.Mod4Mask|X.Mod2Mask,True,X.ButtonPressMask| X.ButtonReleaseMask| X.PointerMotionMask,X.GrabModeAsync,X.GrabModeAsync,X.NONE,X.NONE,)
screen.root.grab_button(X.Button1,X.Mod4Mask,True,X.ButtonPressMask| X.ButtonReleaseMask| X.PointerMotionMask,X.GrabModeAsync,X.GrabModeAsync,X.NONE,X.NONE,)

screen.root.grab_button(X.Button3,X.Mod4Mask|X.Mod2Mask|X.LockMask,True,X.ButtonPressMask| X.ButtonReleaseMask| X.PointerMotionMask,X.GrabModeAsync,X.GrabModeAsync,X.NONE,X.NONE,)
screen.root.grab_button(X.Button3,X.Mod4Mask|X.LockMask,True,X.ButtonPressMask| X.ButtonReleaseMask| X.PointerMotionMask,X.GrabModeAsync,X.GrabModeAsync,X.NONE,X.NONE,)
screen.root.grab_button(X.Button3,X.Mod4Mask|X.Mod2Mask,True,X.ButtonPressMask| X.ButtonReleaseMask| X.PointerMotionMask,X.GrabModeAsync,X.GrabModeAsync,X.NONE,X.NONE,)
screen.root.grab_button(X.Button3,X.Mod4Mask,True,X.ButtonPressMask| X.ButtonReleaseMask| X.PointerMotionMask,X.GrabModeAsync,X.GrabModeAsync,X.NONE,X.NONE,)
font = dpy.open_font('cursor')           # 開啟 cursor 字型
cursor = font.create_glyph_cursor(
    font,                                # mask 用同一個 font
    Xcursorfont.gumby,                   # 游標形狀(source_char)https://xorg.freedesktop.org/archive/current/doc/libX11/libX11/libX11.html#x_font_cursors
    #Xcursorfont.left_ptr,
    Xcursorfont.gumby+1,                 # mask_char(通常是 +1)
    (0, 0, 0),                           # 前景色 RGB(黑色)
    (65535, 65535, 65535)                # 背景色 RGB(白色)
)

red = screen.default_colormap.alloc_named_color("red").pixel
green = screen.default_colormap.alloc_named_color("green").pixel
root.change_attributes(
    cursor=cursor,
    event_mask=X.SubstructureRedirectMask
)
status=None
focused_win=None
#current_workspace=0
#workspace={"1":[],}
while True:
    event=dpy.next_event()
    if event.type==X.ButtonPress and event.child!=X.NONE:
        #event.child.change_attributes(border_pixel=green)
        status=event
        #滑鼠原本的xy
        mc_origin_x=event.root_x
        mc_origin_y=event.root_y
        
        if status.detail==X.Button1:
            #視窗原本的xy
            win_origin_x=event.child.get_geometry().x
            win_origin_y=event.child.get_geometry().y
        elif status.detail==X.Button3:
            #視窗原本的寬高
            win_origin_width=event.child.get_geometry().width
            win_origin_height=event.child.get_geometry().height
    elif event.type==X.MotionNotify and status!=None:
        #滑鼠移動後的xy
        moved_x=event.root_x
        moved_y=event.root_y
        
        #滑鼠了xy移動多少
        move_x=moved_x-mc_origin_x
        move_y=moved_y-mc_origin_y
        if status.detail==X.Button1:
            status.child.configure(
                x=win_origin_x+move_x,#視窗原本的位置+滑鼠移動了多少(也就是要移動多少)
                y=win_origin_y+move_y
            )
        elif status.detail==X.Button3:
            #最小視窗寬高常數(最小可設定為1),根據PEP8規定常數名為全大寫,用底線分隔
            MIN_WIN_WIDTH=50
            MIN_WIN_HEIGHT=50

            status.child.configure(
                width=max(win_origin_width+move_x,MIN_WIN_WIDTH),#視窗原本的大小+滑鼠移動了多少,max()避免視窗大小小於最小視窗寬高常數
                height=max(win_origin_height+move_y,MIN_WIN_HEIGHT)
            )
    elif event.type==X.ButtonRelease and event.child!=X.NONE:
        #event.child.change_attributes(border_pixel=red)
        status=None
    #elif event.type==X.KeyPress:
    #    if event.detail==1_code:
            
    elif event.type==X.MapRequest:
        window=event.window
        window.change_attributes(
            border_pixel=red,
            event_mask=X.EnterWindowMask|X.LeaveWindowMask
        )
        window.configure(border_width=3)
        #workspace[current_workspace].append(window)
        window.map()
    elif event.type==X.EnterNotify:
        focus_win=event.window#要聚焦的視窗
        if focused_win!=None and focused_win.id != focus_win.id:#如果現在聚焦的視窗是其他視窗就把焦點給他並且邊框變色
            focused_win.change_attributes(border_pixel=red)
        focus_win.set_input_focus(
            X.RevertToParent,
            X.CurrentTime
        )
        focus_win.change_attributes(border_pixel=green)#把視窗邊框變色
        focus_win.configure(stack_mode=X.Above)
        focused_win=focus_win#最後聚焦的視窗
    elif event.type==X.KeyPress:
        if event.detail==q_code and focused_win!=None:
            #print(f"kill client:{focused_win.get_wm_name()}")
            #focused_win.kill_client()
            
            win_support_p=focused_win.get_wm_protocols()
            WM_PROTOCOLS = dpy.intern_atom("WM_PROTOCOLS")
            WM_DELETE_WINDOW = dpy.intern_atom("WM_DELETE_WINDOW")
            if WM_DELETE_WINDOW in win_support_p:
                print(f"{focused_win.get_wm_name()} support WM_DELETE_WINDOW")
                cm = xevent.ClientMessage(
                    window = focused_win,
                    client_type = WM_PROTOCOLS,
                    data = (32, [WM_DELETE_WINDOW, X.CurrentTime, 0, 0, 0])
                )
                focused_win.send_event(cm,event_mask=0, propagate=False)
                focused_win=None
            else:
                print(f"{focused_win.get_wm_name()} unsupport WM_DELETE_WINDOW,excute kill_client()")
                focused_win.kill_client()
                focused_win=None
            #event.child是滑鼠底下的視窗
    #elif event.type==X.LeaveNotify:
    #    event.window.change_attributes(border_pixel=red)
    dpy.flush()

上一篇
Day 16|快捷鍵可以讓滑鼠不在視窗就可以關掉
下一篇
Day 18|今天沒時間
系列文
用python做一個X11 WM 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言