<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      X11 設置多屏下窗口在哪個屏幕上全屏

      在 X11 里面,根據 Window Manager Protocols - Extended Window Manager Hints 文檔說明,可使用 _NET_WM_FULLSCREEN_MONITORS 設置窗口應該在哪個屏幕上進行全屏顯示

      其使用方法如下:

      • 在窗口 XMapWindow 之后調用
      • 配合 _NET_WM_STATE_FULLSCREEN 使用
      • 通過 ClientMessage 發送 _NET_WM_FULLSCREEN_MONITORS 給到 RootWindow 設置全屏所在屏幕,其中參數信息如下

      要求傳入 4 個參數,分別是上下、左右四個邊角所在的屏幕的索引號。標重點,這里的上下左右不是要像素值,而是顯示器屏幕的索引號

      根據官方文檔說明,屏幕的索引號可通過 Xinerama extension 獲取到。然而 Xinerama 十分古老,現在可以使用 XRRGetMonitors 來獲取

      設計上,可以給上下、左右四個邊角傳入相同的顯示器屏幕索引,從而讓窗口在指定的顯示器屏幕上全屏顯示。也可以給上下、左右四個邊角傳入不同的顯示器屏幕索引,從而實現跨多個屏幕全屏顯示

      核心調用示例代碼如下,以下代碼需要在窗口 XMapWindow 之后調用

          public void SetFullScreenMonitor(int monitorIndex)
          {
              // [Window Manager Protocols | Extended Window Manager Hints](https://specifications.freedesktop.org/wm-spec/1.5/ar01s06.html )
              // 6.3 _NET_WM_FULLSCREEN_MONITORS
              // A read-only list of 4 monitor indices indicating the top, bottom, left, and right edges of the window when the fullscreen state is enabled. The indices are from the set returned by the Xinerama extension.
              // Windows transient for the window with _NET_WM_FULLSCREEN_MONITORS set, such as those with type _NEW_WM_WINDOW_TYPE_DIALOG, are generally expected to be positioned (e.g. centered) with respect to only one of the monitors. This might be the monitor containing the mouse pointer or the monitor containing the non-full-screen window.
              // A Client wishing to change this list MUST send a _NET_WM_FULLSCREEN_MONITORS client message to the root window. The Window Manager MUST keep this list updated to reflect the current state of the window.
      
              var wmState = XInternAtom(Display, "_NET_WM_FULLSCREEN_MONITORS", true);
              Console.WriteLine($"_NET_WM_FULLSCREEN_MONITORS={wmState}");
      
              // 如 https://github.com/underdoeg/ofxFenster/blob/6ecd5bd9b8412f98e1c4e73433e2aade2b5902c4/src/ofxFenster.cpp#L691 的代碼所示。這里傳入的 Left、Top、Right、Bottom 不是像素的值,而是屏幕的索引值
      
              // _NET_WM_FULLSCREEN_MONITORS, CARDINAL[4]/32
              /*
               data.l[0] = the monitor whose top edge defines the top edge of the fullscreen window
               data.l[1] = the monitor whose bottom edge defines the bottom edge of the fullscreen window
               data.l[2] = the monitor whose left edge defines the left edge of the fullscreen window
               data.l[3] = the monitor whose right edge defines the right edge of the fullscreen window
              */
              // 這里的 Left、Top、Right、Bottom 是屏幕的索引值,而不是像素的值
      
              var left = monitorIndex;
              var top = monitorIndex;
              var right = monitorIndex;
              var bottom = monitorIndex;
      
              Console.WriteLine($"Left={left} Top={top} Right={right} Bottom={bottom}");
      
              //int[] monitorEdges = [top, bottom, left, right];
              //XChangeProperty(Display, X11Window, wmState, (IntPtr) Atom.XA_CARDINAL, format: 32, PropertyMode.Replace,
              //    monitorEdges, monitorEdges.Length);
      
              // A Client wishing to change this list MUST send a _NET_WM_FULLSCREEN_MONITORS client message to the root window. The Window Manager MUST keep this list updated to reflect the current state of the window.
              var xev = new XEvent
              {
                  ClientMessageEvent =
                  {
                      type = XEventName.ClientMessage,
                      send_event = true,
                      window = X11Window,
                      message_type = wmState,
                      format = 32,
                      ptr1 = top,
                      ptr2 = bottom,
                      ptr3 = left,
                      ptr4 = right,
                  }
              };
      
              XSendEvent(Display, RootWindow, false,
                  new IntPtr((int) (EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
          }
      

      如上文所述,單獨通過 ClientMessage 發送 _NET_WM_FULLSCREEN_MONITORS 給到 RootWindow 是沒有效果的,需要配合 _NET_WM_STATE_FULLSCREEN 使用。發送 _NET_WM_STATE_FULLSCREEN 的示例代碼如下,同樣以下代碼也應該在窗口 XMapWindow 之后調用

          public void SetFullScreen()
          {
              var hiddenAtom = XInternAtom(Display, "_NET_WM_STATE_HIDDEN", true);
              var fullScreenAtom = XInternAtom(Display, "_NET_WM_STATE_FULLSCREEN", true);
      
              ChangeWMAtoms(false, hiddenAtom);
              ChangeWMAtoms(true, fullScreenAtom);
          }
      
          private void ChangeWMAtoms(bool enable, params IntPtr[] atoms)
          {
              if (atoms.Length != 1 && atoms.Length != 2)
              {
                  throw new ArgumentException();
              }
      
              var wmState = XInternAtom(Display, "_NET_WM_STATE", true);
      
              SendNetWMMessage(wmState,
                  (IntPtr) (enable ? 1 : 0),
                  atoms[0],
                  atoms.Length > 1 ? atoms[1] : IntPtr.Zero,
                  atoms.Length > 2 ? atoms[2] : IntPtr.Zero,
                  atoms.Length > 3 ? atoms[3] : IntPtr.Zero
               );
          }
      
          private void SendNetWMMessage(IntPtr message_type, IntPtr l0,
              IntPtr? l1 = null, IntPtr? l2 = null, IntPtr? l3 = null, IntPtr? l4 = null)
          {
              var xev = new XEvent
              {
                  ClientMessageEvent =
                  {
                      type = XEventName.ClientMessage,
                      send_event = true,
                      window = X11Window,
                      message_type = message_type,
                      format = 32,
                      ptr1 = l0,
                      ptr2 = l1 ?? IntPtr.Zero,
                      ptr3 = l2 ?? IntPtr.Zero,
                      ptr4 = l3 ?? IntPtr.Zero
                  }
              };
              xev.ClientMessageEvent.ptr4 = l4 ?? IntPtr.Zero;
              XSendEvent(Display, RootWindow, false,
                  new IntPtr((int) (EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev);
          }
      

      本文接下來將使用實際的代碼給大家演示 _NET_WM_FULLSCREEN_MONITORS 的調用方法,需求是在一個包含雙屏的設備上,每個屏幕分別顯示一個全屏的窗口

      本文的演示是在 UOS 上進行的,系統信息如下

      $ cat /etc/os-release
      PRETTY_NAME="UnionTech OS Desktop 20 E"
      NAME="uos"
      VERSION_ID="20"
      VERSION="20"
      ID=uos
      HOME_URL="https://www.chinauos.com/"
      BUG_REPORT_URL="http://bbs.chinauos.com"
      VERSION_CODENAME=uranus
      
      $ cat /etc/os-version
      [Version]
      SystemName=UnionTech OS Desktop
      SystemName[zh_CN]=統信桌面操作系統
      ProductType=Desktop
      ProductType[zh_CN]=桌面
      EditionName=E
      EditionName[zh_CN]=E
      MajorVersion=20
      MinorVersion=1050
      OsBuild=11068.102
      

      處理器 CPU 信息如下

      $ cat /proc/cpuinfo
      processor       : 0
      vendor_id       : CentaurHauls
      cpu family      : 7
      model           : 59
      model name      : ZHAOXIN KaiXian KX-U6780A@2.7GHz
      ...
      

      使用 xrandr 命令可查看到的雙屏信息如下

      $ xrandr --listmonitors
      Monitors: 2
       0: +*DisplayPort-1 1920/708x1080/398+1920+0  DisplayPort-1
       1: +DisplayPort-0 1920/708x1080/398+0+0  DisplayPort-0
      

      先使用 XRRGetMonitors 獲取多個屏幕的信息,本文這里直接抄 Avalonia 的 Randr15ScreensImpl 類,代碼如下

      // Copy from https://github.com/AvaloniaUI/Avalonia \src\Avalonia.X11\Screens\X11Screen.Providers.cs
      
      public class Randr15ScreensImpl
      {
          public Randr15ScreensImpl(nint display, nint rootWindow)
          {
              _display = display;
              var eventWindow = CreateEventWindow(display, rootWindow);
              _window = eventWindow;
      
              XRRSelectInput(display, _window, RandrEventMask.RRScreenChangeNotify);
          }
      
          public unsafe MonitorInfo[] GetMonitorInfos()
          {
              XRRMonitorInfo* monitors = XRRGetMonitors(_display, _window, true, out var count);
              var screens = new MonitorInfo[count];
              for (var c = 0; c < count; c++)
              {
                  var mon = monitors[c];
      
                  var outputs = new nint[mon.NOutput];
      
                  for (int i = 0; i < outputs.Length; i++)
                  {
                      outputs[i] = mon.Outputs[i];
                  }
      
                  screens[c] = new MonitorInfo()
                  {
                      Name = mon.Name,
                      IsPrimary = mon.Primary != 0,
                      X = mon.X,
                      Y = mon.Y,
                      Width = mon.Width,
                      Height = mon.Height,
                      Outputs = outputs,
                      Display = _display,
                  };
              }
      
              return screens;
          }
      
          private readonly IntPtr _display;
      
          private readonly IntPtr _window;
      }
      
      public unsafe struct MonitorInfo
      {
          public IntPtr Name;
          public bool IsPrimary;
          public int X;
          public int Y;
          public int Width;
          public int Height;
          public IntPtr[] Outputs;
          public IntPtr Display { get; init; }
      
          public override string ToString()
          {
              var namePtr = XGetAtomName(Display, Name);
                  var name = Marshal.PtrToStringAnsi(namePtr);
              XFree(namePtr);
      
              return $"{name}({Name}) IsPrimary={IsPrimary} XY={X},{Y} WH={Width},{Height}";
          }
      }
      

      獲取到的屏幕順序十分重要,因為接下來調用 _NET_WM_FULLSCREEN_MONITORS 將傳遞參數為顯示器序號

      為了方便地創建 X11 窗口,本文這里封裝了名為 TestX11Window 的窗口輔助類,其構造函數和成員屬性如下

      internal class TestX11Window
      {
          public TestX11Window(string name, int x, int y, int width, int height, nint display, nint rootWindow, int screen)
          {
              Name = name;
              Display = display;
      
              XMatchVisualInfo(display, screen, 32, 4, out var info);
              var visual = info.visual;
      
              var valueMask =
                      //SetWindowValuemask.BackPixmap
                      0
                      | SetWindowValuemask.BackPixel
                      | SetWindowValuemask.BorderPixel
                      | SetWindowValuemask.BitGravity
                      | SetWindowValuemask.WinGravity
                      | SetWindowValuemask.BackingStore
                      | SetWindowValuemask.ColorMap
                  //| SetWindowValuemask.OverrideRedirect
                  ;
              var xSetWindowAttributes = new XSetWindowAttributes
              {
                  backing_store = 1,
                  bit_gravity = Gravity.NorthWestGravity,
                  win_gravity = Gravity.NorthWestGravity,
                  //override_redirect = true, // 設置窗口的override_redirect屬性為True,以避免窗口管理器的干預
                  colormap = XCreateColormap(display, rootWindow, visual, 0),
                  border_pixel = 0,
                  background_pixel = 0,
              };
      
              var handle = XCreateWindow(display, rootWindow, x, y, width, height, 5,
                  32,
                  (int)CreateWindowArgs.InputOutput,
                  visual,
                  (nuint)valueMask, ref xSetWindowAttributes);
      
              X11Window = handle;
      
              XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask |
                                       XEventMask.PointerMotionHintMask;
              var mask = new IntPtr(0xffffff ^ (int)ignoredMask);
              XSelectInput(display, handle, mask);
      
              var gc = XCreateGC(display, handle, 0, 0);
              GC = gc;
      
              X = x;
              Y = y;
      
              Width = width;
              Height = height;
      
              RootWindow = rootWindow;
          }
      
          public string Name { get; }
      
          public IntPtr X11Window { get; }
      
          public IntPtr Display { get; }
          public IntPtr GC { get; }
      
          public int X { get; }
          public int Y { get; }
      
          public int Width { get; }
          public int Height { get; }
      
          public IntPtr RootWindow { get; }
      }
      

      以上代碼中創建窗口的部分是比較標準的做法,沒有什么特殊處理。如大家對以上創建窗口細節感興趣,還請參閱 了解 X11 窗口和消息基礎知識 博客

      通過以上代碼可見在構造函數里面創建了窗口,但是沒有經過 XMapWindow 顯示出來。再添加一個名為 MapWindow 的方法,用于顯示出窗口

      internal class TestX11Window
      {
          public void MapWindow()
          {
              XMapWindow(Display, X11Window);
              XFlush(Display);
          }
      }
      

      以上代碼里面默認引用了 XLib 的 PInvoke 方法,這部分代碼的方法定義和 XLib 相同,就不在本文列舉出來,如需要全部的項目文件,可到本文末尾找到本文所有代碼的下載方法

      為了能夠在窗口上顯示點東西,咱繼續添加一個名為 Draw 的方法,刪減之后的代碼如下

      internal class TestX11Window
      {
          public void Draw()
          {
              XImage xImage = CreateImage();
      
              XPutImage(Display, X11Window, GC, ref xImage, 0, 0, 0, 0, (uint) Width,
                  (uint) Height);
          }
      }
      

      創建圖片的 CreateImage 不屬于本文關注的內容,還請忽略,只需要了解到有一個方法能夠創建出 XImage 即可

      準備工作完成之后,回到 Program.cs 主函數里面,接下來咱將為每個顯示器屏幕創建一個窗口,其代碼如下

      XInitThreads();
      var display = XOpenDisplay(IntPtr.Zero);
      var screen = XDefaultScreen(display);
      var rootWindow = XDefaultRootWindow(display);
      
      var dictionary = new Dictionary<IntPtr, TestX11Window>();
      
      var randr15ScreensImpl = new Randr15ScreensImpl(display, rootWindow);
      var monitorInfos = randr15ScreensImpl.GetMonitorInfos();
      for (var i = 0; i < monitorInfos.Length; i++)
      {
          // 屏幕0 DisplayPort-1(343) IsPrimary=True XY=1920,309 WH=1920,1080
          // 屏幕1 DisplayPort-0(626) IsPrimary=False XY=0,0 WH=1920,1080
          MonitorInfo monitorInfo = monitorInfos[i];
          Console.WriteLine($"屏幕{i} {monitorInfo}");
      
          var x = monitorInfo.X;
          var y = monitorInfo.Y;
      
          var width = monitorInfo.Width;
          var height = monitorInfo.Height;
      
          var testX11Window = new TestX11Window($"Window{i}", x, y, width, height, display, rootWindow, screen);
      
          testX11Window.MapWindow();
          testX11Window.Draw();
      
          dictionary[testX11Window.X11Window] = testX11Window;
      
          Console.WriteLine($"X11Window{i}={testX11Window.X11Window}");
      }
      

      現在如果嘗試跑起來應用,則會發現窗口似乎隨機顯示到某個屏幕上。如果更細心一點,會發現窗口將會顯示到鼠標最后一次落下的屏幕上。或觸摸最后點擊到的屏幕上

      上一篇博客 中,采用 XSetWMNormalHints 固定窗口所在的屏幕,此方法可以決定窗口應該在哪個屏幕上顯示

      在本文里面,將不使用 XSetWMNormalHints 的方法,而是只采用 _NET_WM_FULLSCREEN_MONITORS 進行設置

      如本文一開始所述,單獨設置 _NET_WM_FULLSCREEN_MONITORS 是沒有效果的,需要配合 _NET_WM_STATE_FULLSCREEN 使用

      給 TestX11Window 再添加 SetFullScreen 和 SetFullScreenMonitor 方法,分別用于設置全屏和控制在哪個窗口全屏。具體實現在上文已經列舉出來了,這里就不再重復說明

      添加之后,繼續修改 Program.cs 函數,在創建完成窗口之后,先調用 MapWindow 再分別設置窗口全屏

          var testX11Window = new TestX11Window($"Window{i}", x, y, width, height, display, rootWindow, screen);
      
          testX11Window.MapWindow();
      
          testX11Window.SetFullScreen();
          testX11Window.SetFullScreenMonitor(i);
      

      嘗試運行代碼,可見在雙屏設備上,每個屏幕分別顯示一個全屏的窗口

      本文代碼放在 githubgitee 上,可以使用如下命令行拉取代碼。我整個代碼倉庫比較龐大,使用以下命令行可以進行部分拉取,拉取速度比較快

      先創建一個空文件夾,接著使用命令行 cd 命令進入此空文件夾,在命令行里面輸入以下代碼,即可獲取到本文的代碼

      git init
      git remote add origin https://gitee.com/lindexi/lindexi_gd.git
      git pull origin 951e0cc432ee948c71bb4365d56e1ae8eb43d502
      

      以上使用的是國內的 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令行繼續輸入以下代碼,將 gitee 源換成 github 源進行拉取代碼。如果依然拉取不到代碼,可以發郵件向我要代碼

      git remote remove origin
      git remote add origin https://github.com/lindexi/lindexi_gd.git
      git pull origin 951e0cc432ee948c71bb4365d56e1ae8eb43d502
      

      獲取代碼之后,進入 X11/HairkallberciderqewallReedeegewhige 文件夾,即可獲取到源代碼

      更多 X11 技術博客,請參閱 博客導航

      posted @ 2025-08-08 08:49  lindexi  閱讀(93)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久夜色噜噜噜亚洲av| 99国产欧美另类久久久精品| 又黄又爽又色的免费网站| 久久久久成人精品无码中文字幕| 亚洲国产精品久久久久婷婷老年 | 人妻激情另类乱人伦人妻| 亚洲国产综合精品2020| 成年女人黄小视频| 日本中文一二区有码在线| 久久久久久综合网天天| 99国产欧美另类久久久精品| 精品黄色av一区二区三区| 无码人妻精品一区二区三区下载| 日韩国产中文字幕精品| 国产精品亚洲综合色区丝瓜| 国产成人精品a视频| 通山县| 国产精品大片中文字幕| 亚洲男人天堂2018| 果冻传媒董小宛视频| 四虎永久在线精品8848a| 亚洲伊人久久精品影院| 国产毛片精品av一区二区 | 国语精品国内自产视频| 国产精自产拍久久久久久蜜| 武隆县| 久热伊人精品国产中文| 日日爽日日操| 综1合AV在线播放| 香港日本三级亚洲三级| 精品国产av无码一区二区三区| 亚洲天堂领先自拍视频网| 亚洲人成电影在线天堂色| 亚洲精品日韩中文字幕| 国产成人av三级在线观看| 国产精品毛片一区二区| 国产av成人精品播放| 久久99精品国产自在现线小黄鸭| 亚洲精品一区二区天堂| 起碰免费公开97在线视频| 欧美亚洲国产一区二区三区|