简体   繁体   中英

Debian+Unity: Game in "full screen" extended to two screen

I'm working on a Unity project in * Linux where I need to display the game/project on 2 screens using only 1 camera and 1 "display" , for better performance. What can i do to solve this problem? I will need set or modify something in the OS? Can i do it programmatically?

I'm using:

  • Debian with XFCE
  • Unity3d 2020.3.16f1
  • Rendering Pipeline: URP 2D

Edit:
For a exemple: I want to "combine" both monitors/display to act as one. So when i have two screen of 1920x1080 the game need to "extend" the window and work as if the screen was 1920x2160.

Current i tried to used the follow command in unity:

Screen.SetResolution(int width, int height, FullScreenMode 
fullscreenMode);

with:

  • Width: 1920
  • Height: 2160
  • FullScreenMode: ExclusiveFullScreen, FullScreenWindow ,MaximizedWindow, Windowed.

In all the modes the game stayed in only one screen, liming it self to 1920x1080.

In Unity 2020.3.16f1, I couldn't find any direct solution for Linux (Debian).


The solution I found is to meddle with the X windows server, Xorg has been used as default since Debian 4.0.

The option I used: Xorg has a module called Xrandr (Linux users probably already know that) that provides the xrandr commands in terminal and interacts with the configuration of the displays.

To check what displays are connected I use the command xrandr --listmonitors | grep '+' | awk {'print $4'} xrandr --listmonitors | grep '+' | awk {'print $4'} xrandr --listmonitors | grep '+' | awk {'print $4'} . For me it return:

  • HDMI-0
  • HDMI-1

So to combine the display use the command xrandr --setmonitor CustomName auto HDMI-0,HDMI-1} , this create a virtual display.

As I want to set one screen above the other I use this xrandr --output HDMI-1 --above HDMI-0 .

Now both display work as one.


I want Unity to call these commands, so that when the game opens it sets the configuration automatically, but because I am using the IL2CPP Scripting Backend I can't use Application.Process to call the commands. To solve that I create a lib in C++ 11 , libSystemCommands.so , to execute the commands.

system_commands.cpp

#include "system_commands.h"
#include <vector>
#include <array>
#include <memory>
#include <cstring>

void system_command(const char * command){
    std::system(command);
}

char*  system_command_with_output(const char * command){
    std::array<char, 128> buffer;
    std::string  result;
    std::unique_ptr<FILE , decltype(&pclose)> pipe(popen(command,"r"), pclose);
    if (!pipe) throw  std::runtime_error("popopen() failed!");
    while (fgets(buffer.data(),buffer.size(), pipe.get())!= nullptr)
        result += buffer.data();
    char * res = (char *) malloc(strlen(result.c_str())+1);
    strcpy(res,result.c_str());
    return res;
}

system_commands.h

#ifndef SYSTEMCOMMAND_LIBRARY_H
#define SYSTEMCOMMAND_LIBRARY_H

#if defined(_MSC_VER)

#ifdef NativePlugin_EXPORTS
#define NATIVEPLUGIN_API extern "C" __declspec(dllexport)
#else
#define NATIVEPLUGIN_API extern "C" __declspec(dllimport)
#endif
#else
#define NATIVEPLUGIN_API extern "C"
#endif

NATIVEPLUGIN_API void system_command(const char * command);

NATIVEPLUGIN_API char* system_command_with_output(const char * command);

#endif //SYSTEMCOMMAND_LIBRARY_H

In Unity I create the following script to control the changes:

DisplayManager.cs

using System;
using System.Runtime.InteropServices;

public static class DisplayManager
{
    //List of displays/monitors connected
    private static string[] _displays;
    private static string[] _originalPositions;

    //Detect the displays/monitors connected, using xrandr
    public static void DetectDisplays()
    {
        //Get and set the display connected, to a list.
        string result = system_command_with_output("xrandr --listmonitors | grep '+' | awk {'print $4'}");
        _displays = result.Split(new string[] { Environment.NewLine }, 
            StringSplitOptions.RemoveEmptyEntries);
        // We want to run the rest only if there is two or more screens.
        if (_displays.Length<=1) return;
        // Get the original position of the two first display/monitors.
        result = system_command_with_output(
            "xrandr --listmonitors | grep '+' | awk '{print $3}' | awk -F'+' '{print $2,$3}'");
        result = result.Replace(' ', 'x');
        _originalPositions = result.Split(new string[] { Environment.NewLine }, 
            StringSplitOptions.RemoveEmptyEntries);
    }

    //Combine two displays/monitors in one using xrandr
    public static void CombineDisplays()
    {
        // We want to run this only if there is two or more screens.
        if (_displays.Length<=1) return;
        // xrandr command to create a virtual monitor combining others monitors.
        system_command($"xrandr --setmonitor DisplayCombination auto {_displays[0]},{_displays[1]}");
        // xrandr don´t apply the new virtual monitor automatically and don´t set a change if the input
        // is equal of the current state. So the two command is to make sure to apple de combination and
        // the display is one above another.
        system_command($"xrandr --auto & xrandr --output {_displays[1]} --left-of {_displays[0]}");
        // Set the desired position.
        system_command($"xrandr --auto & xrandr --output {_displays[1]} --above {_displays[0]}");
    }

    //Reset the Display to before the Combination.
    public static void ResetDisplays()
    {
        // Delete the virtual display created, if it exist
        system_command("xrandr --delmonitor DisplayCombination");
        // We want to run this only if there is two or more screens.
        if (_displays.Length<=1) return;
        // xrandr don´t apply the deletion of the virtual display automatically and don´t set a change
        // if the input is equal of the current state. So the two command is to make sure to apple de
        // deletion and reset the displays/monitors to the original position.
        system_command($"xrandr --auto & xrandr --output {_displays[1]} --left-of {_displays[0]}");
        //Set the Displays to their original position.
        for(int i = 0; i <_displays.Length;i++)
            system_command($"xrandr --output {_displays[i]} --pos {_originalPositions[i]}");
    }
    
    // function from the lib that intermediate the command execution with the system. 
    [DllImport("SystemCommands")]
    private static  extern void system_command(string command);
    // function from the lib that intermediate the command execution with the system, with output.
    [DllImport("SystemCommands")]
    private static  extern string system_command_with_output(string command);
}

And then in another script I only need to call this method and set the Screen.SetResolution to do the trick.

Example:

private int _newWidth;
private int _newHeight;
private void Awake()
{
    _newWidth = Display.main.systemWidth;
    _newHeight = Display.main.systemHeight;
    if (Display.displays.Length > 1)
        _newHeight += Display.displays[1].systemHeight;
    DisplayManager.DetectDisplays();
    DisplayManager.CombineDisplays();
}

private void Start()
{
    Screen.SetResolution(_newWidth,_newHeight,FullScreenMode.FullScreenWindow);
}

private void OnApplicationQuit()
{
    DisplayManager.ResetDisplays();
}

The other option (I didn't check): Is using the Xorg configuration folder, that is read when you login in account, together with the module Xinerama (I need to check if Xinerama is really needed).

Create a file at Xorg.conf.d , as ##-name.conf (“##” being a number identifies the priority of execution/reading, and “name” being something you can use to identify it more easily).

The folder can be at:

  • /etc/X11/Xorg.conf.d/
  • /user/shared/X11/Xorg.conf.d/

And you will need to setup a SERVERLAYOUT , and enable the option Xinerama.

Section "ServerLayout"
Identifier "name"
Screen "screen−id"
...
Option  "Xinerama"  "1"
...
EndSection

More information abou xorg at: https://www.x.org/releases/current/doc/man/man5/xorg.conf.5.xhtml#heading16 https://wiki.archlinux.org/title/multihead

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM