Skip to content

File TouchMonitor.cpp

File List > AIAC > TouchMonitor.cpp

Go to the documentation of this file

// #####################################################################
// >>>>>>>>>>>>>>>>>>>>> BEGINNING OF LEGAL NOTICE >>>>>>>>>>>>>>>>>>>>>
//######################################################################
//
// This source file, along with its associated content, was authored by
// Andrea Settimi, Hong-Bin Yang, Naravich Chutisilp, and numerous other
// contributors. The code was originally developed at the Laboratory for
// Timber Construction (IBOIS, director: Prof. Yves Weinand) at the School of 
// Architecture, Civil and Environmental Engineering (ENAC) at the Swiss
// Federal Institute of Technology in Lausanne (EPFL) for the Doctoral
// Research "Augmented Carpentry" (PhD researcher: Andrea Settimi,
// co-director: Dr. Julien Gamerro, director: Prof. Yves Weinand).
//
// Although the entire repository is distributed under the GPL license,
// these particular source files may also be used under the terms of the
// MIT license. By accessing or using this file, you agree to the following:
//
// 1. You may reproduce, modify, and distribute this file in accordance
//    with the terms of the MIT license.
// 2. You must retain this legal notice in all copies or substantial
//    portions of this file.
// 3. This file is provided "AS IS," without any express or implied
//    warranties, including but not limited to the implied warranties of
//    merchantability and fitness for a particular purpose.
//
// If you cannot or will not comply with the above conditions, you are
// not permitted to use this file. By proceeding, you acknowledge and
// accept all terms and conditions herein.
//
//######################################################################
// <<<<<<<<<<<<<<<<<<<<<<< END OF LEGAL NOTICE <<<<<<<<<<<<<<<<<<<<<<<<
// #####################################################################
//
#include "aiacpch.h"

#include "AIAC/TouchMonitor.h"
#include "AIAC/Log.h"
#include "AIAC/Config.h"

#include "stb/stb_image.h"
#include "stb/stb_image_write.h"

namespace AIAC
{
    static bool IsGlfwInitialized()
    {
        (void)glfwGetKeyScancode(0);
        return glfwGetError(NULL) != GLFW_NOT_INITIALIZED;
    }

    static void GLFWMonitorCallback(GLFWmonitor* monitor, int event)
    {
        if (event == GLFW_CONNECTED) AIAC_INFO("Monitor connected: {0}", glfwGetMonitorName(monitor));
        else if (event == GLFW_DISCONNECTED) AIAC_INFO("Monitor disconnected: {0}", glfwGetMonitorName(monitor));
    }

    TouchMonitor::TouchMonitor()
    {
        Init();
    }

    TouchMonitor::TouchMonitor(bool isPrintMonitorsInfo)
    : m_IsPrintMonitorsInfo(isPrintMonitorsInfo)
    {
        Init();
    }

    void TouchMonitor::PrintInfoMonitors(int count, GLFWmonitor**& monitors)
    {
        int width_mm, height_mm;
        float xScale, yScale;
        int xPos, yPos;
        int xpos, ypos, width, height;
        for (int i = 0; i < count; i++)
        {
            GLFWmonitor* monitor = monitors[i];

            const GLFWvidmode* mode = glfwGetVideoMode(monitor);
            AIAC_INFO("Monitor {0}: {1}x{2}@{3}Hz", i, mode->width, mode->height, mode->refreshRate);

            glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm);
            AIAC_INFO("Monitor {0} physical size: {1}x{2}", i, width_mm, height_mm);

            glfwGetMonitorContentScale(monitor, &xScale, &yScale);
            AIAC_INFO("Monitor {0} content scale: {1}x{2}", i, xScale, yScale);

            glfwGetMonitorPos(monitor, &xPos, &yPos);
            AIAC_INFO("Monitor {0} position: {1}, {2}", i, xPos, yPos);

            glfwGetMonitorWorkarea(monitor, &xpos, &ypos, &width, &height);
            AIAC_INFO("Monitor {0} workarea: {1}, {2}, {3}, {4}", i, xpos, ypos, width, height);

            const char* name = glfwGetMonitorName(monitor);
            AIAC_INFO("Monitor {0} name: {1}", i, name);
        }
    }

    inline std::string TouchMonitor::ImportNameFromConfig() {
        return  AIAC::Config::Get<std::string>(Config::SEC_TOUCH_MONITOR_SPECS, Config::NAME, "WaveShare WS170120"); }
    inline std::string TouchMonitor::ImportVideoModeFromConfig() {
        return AIAC::Config::Get<std::string>(Config::SEC_TOUCH_MONITOR_SPECS, Config::LINK_MODE, "HDMI"); }
    inline std::string TouchMonitor::ImportResolutionFromConfig(){
        return AIAC::Config::Get<std::string>(Config::SEC_TOUCH_MONITOR_SPECS, Config::RESOLUTION, "800x400"); }

    void TouchMonitor::Init()
    {
        m_Name = ImportNameFromConfig();
        m_VideoMode = ImportVideoModeFromConfig();
        m_Resolution = ImportResolutionFromConfig();
        m_IsConnected = false;

        if (IsGlfwInitialized) { AIAC_ASSERT(glfwInit(), "Could not initialize GLFW!"); }
        else { AIAC_CRITICAL("Multiple windows not supported."); exit(EXIT_FAILURE); }

        int count;
        GLFWmonitor** monitors = glfwGetMonitors(&count);
        if(count<=0) { AIAC_CRITICAL("No monitors found."); exit(EXIT_FAILURE); }
        glfwSetMonitorCallback(GLFWMonitorCallback);

        if(m_IsPrintMonitorsInfo) PrintInfoMonitors(count, monitors);

        if(!SiftMonitorsByMode(m_VideoMode, count, monitors)) { AIAC_CRITICAL("No video mode specified found in list monitors."); exit(EXIT_FAILURE); }
        if(!SiftMonitorsByResolution(m_Resolution, m_SiftedMonitors)) { AIAC_CRITICAL("No resolution specified found in list monitors."); exit(EXIT_FAILURE); }
#if __linux__
        if(!GetMonitorID(m_Name)) { AIAC_CRITICAL("No monitor ID retrieved."); exit(EXIT_FAILURE); }

        if(!MapMonitor(m_ID, m_SiftedMonitors[0])) { AIAC_CRITICAL("Mapping monitor failed."); exit(EXIT_FAILURE); }
#endif
        m_IsConnected = true;
    }

    bool TouchMonitor::SiftMonitorsByMode(std::string videoMode, int count, GLFWmonitor**& monitors)
    {
        bool isVideoModPresent = false;

        for (int i = 0; i < count; i++)
        {  
            AIAC_INFO("Monitor {0} name: {1} looking for {2}", i, glfwGetMonitorName(monitors[i]), videoMode.c_str());
            if (strstr(glfwGetMonitorName(monitors[i]), videoMode.c_str()) != NULL)
            {
                AIAC_INFO("Monitor {0} name: {1} found", i, glfwGetMonitorName(monitors[i]));

                isVideoModPresent = true;
                m_SiftedMonitors.push_back(monitors[i]);
            }
        }
        return isVideoModPresent;
    }

    bool TouchMonitor::SiftMonitorsByResolution(std::string res, std::vector<GLFWmonitor*>& monitors)
    {
        bool isResolutionPresent = false;
        int count = monitors.size();
        for (int i = 0; i < count; i++)
        {
            AIAC_INFO("Monitor {0} name: {1} looking for {2}", i, glfwGetMonitorName(monitors[i]), res.c_str());
            const GLFWvidmode* mode = glfwGetVideoMode(monitors[i]);
            AIAC_INFO("Mode width: {0} height: {1}. Looking for {2} x {3}", mode->width, mode->height, std::stoi(res.substr(0, res.find('x'))), std::stoi(res.substr(res.find('x') + 1)));
            if (!(mode->width == std::stoi(res.substr(0, res.find('x'))) && mode->height == std::stoi(res.substr(res.find('x') + 1))))
            {
                monitors.erase(monitors.begin() + i);
            }
            else isResolutionPresent = true;
        }
        if (!isResolutionPresent || monitors.size() > 1) return false; 
        else if (monitors.size() > 1) return false;
        else return true;
    }

    bool TouchMonitor::GetMonitorID(std::string MonitorName)
    {
        char buffer[1024];
        FILE* Fpipe;

        const char* command = "xinput";
        if ( Fpipe = (FILE*)popen(command, "r") )
        {
            while (fgets(buffer, sizeof(buffer), Fpipe))
            {
                if (strstr(buffer, MonitorName.c_str()))
                {
                    char* MonitorConfig_id_ptr = strstr(buffer, "id=");
                    MonitorConfig_id_ptr = strchr(MonitorConfig_id_ptr, '=') + 1;
                    MonitorConfig_id_ptr[3] = '\0';
                    m_ID = atoi(MonitorConfig_id_ptr);
                    break;
                }
            }
            pclose(Fpipe);
        }
        else { AIAC_CRITICAL("popen() failed"); exit(EXIT_FAILURE);}
        if (m_ID == -1) return false;
        return true;
    }

    bool TouchMonitor::MapMonitor(int32_t monitorID, GLFWmonitor* touchMonitor)
    {
        std::string commandMto = "xinput map-to-output ";
        commandMto += std::to_string(monitorID);
        commandMto += " ";
        commandMto += std::string(glfwGetMonitorName(touchMonitor));

        int commandMto_result = system(commandMto.c_str());
        if (commandMto_result != 0) return false;
        return true;
    }

}