twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

cinnamon_unix.go (3413B)


      1 // Copyright 2020 The Ebiten Authors
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 // +build dragonfly freebsd linux netbsd openbsd solaris
     16 // +build !android
     17 
     18 package devicescale
     19 
     20 import (
     21 	"encoding/xml"
     22 	"os"
     23 	"os/exec"
     24 	"path/filepath"
     25 	"strconv"
     26 
     27 	"github.com/hajimehoshi/ebiten/v2/internal/glfw"
     28 )
     29 
     30 type xmlBool bool
     31 
     32 func (b *xmlBool) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
     33 	var s string
     34 	if err := d.DecodeElement(&s, &start); err != nil {
     35 		return err
     36 	}
     37 	*b = xmlBool(s == "yes")
     38 	return nil
     39 }
     40 
     41 type cinnamonMonitors struct {
     42 	XMLName       xml.Name                        `xml:"monitors"`
     43 	Version       string                          `xml:"version,attr"`
     44 	Configuration []cinnamonMonitorsConfiguration `xml:"configuration"`
     45 }
     46 
     47 type cinnamonMonitorsConfiguration struct {
     48 	BaseScale float64 `xml:"base_scale"`
     49 	Output    []struct {
     50 		X       int     `xml:"x"`
     51 		Y       int     `xml:"y"`
     52 		Width   int     `xml:"width"`
     53 		Height  int     `xml:"height"`
     54 		Scale   float64 `xml:"scale"`
     55 		Primary xmlBool `xml:"primary"`
     56 	} `xml:"output"`
     57 }
     58 
     59 func (c *cinnamonMonitorsConfiguration) matchesWithGLFWMonitors(monitors []*glfw.Monitor) bool {
     60 	type area struct {
     61 		X, Y, Width, Height int
     62 	}
     63 	areas := map[area]struct{}{}
     64 
     65 	for _, o := range c.Output {
     66 		if o.Width == 0 || o.Height == 0 {
     67 			continue
     68 		}
     69 		areas[area{
     70 			X:      o.X,
     71 			Y:      o.Y,
     72 			Width:  o.Width,
     73 			Height: o.Height,
     74 		}] = struct{}{}
     75 	}
     76 
     77 	if len(areas) != len(monitors) {
     78 		return false
     79 	}
     80 
     81 	for _, m := range monitors {
     82 		x, y := m.GetPos()
     83 		v := m.GetVideoMode()
     84 		a := area{
     85 			X:      x,
     86 			Y:      y,
     87 			Width:  v.Width,
     88 			Height: v.Height,
     89 		}
     90 		if _, ok := areas[a]; !ok {
     91 			return false
     92 		}
     93 	}
     94 	return true
     95 }
     96 
     97 func cinnamonScaleFromXML() (float64, error) {
     98 	home, err := os.UserHomeDir()
     99 	if err != nil {
    100 		return 0, err
    101 	}
    102 	f, err := os.Open(filepath.Join(home, ".config", "cinnamon-monitors.xml"))
    103 	if err != nil {
    104 		return 0, err
    105 	}
    106 	defer f.Close()
    107 
    108 	d := xml.NewDecoder(f)
    109 
    110 	var monitors cinnamonMonitors
    111 	if err = d.Decode(&monitors); err != nil {
    112 		return 0, err
    113 	}
    114 
    115 	for _, c := range monitors.Configuration {
    116 		if !c.matchesWithGLFWMonitors(glfw.GetMonitors()) {
    117 			continue
    118 		}
    119 		for _, v := range c.Output {
    120 			// TODO: Get the monitor at the specified position.
    121 			// TODO: Consider the base scale?
    122 			if v.Primary && v.Scale != 0.0 {
    123 				return v.Scale, nil
    124 			}
    125 		}
    126 	}
    127 	return 0, nil
    128 }
    129 
    130 func cinnamonScale() float64 {
    131 	if s, err := cinnamonScaleFromXML(); err == nil && s > 0 {
    132 		return s
    133 	}
    134 
    135 	out, err := exec.Command("gsettings", "get", "org.cinnamon.desktop.interface", "scaling-factor").Output()
    136 	if err != nil {
    137 		if err == exec.ErrNotFound {
    138 			return 0
    139 		}
    140 		if _, ok := err.(*exec.ExitError); ok {
    141 			return 0
    142 		}
    143 		panic(err)
    144 	}
    145 	m := gsettingsRe.FindStringSubmatch(string(out))
    146 	s, err := strconv.Atoi(m[1])
    147 	if err != nil {
    148 		return 0
    149 	}
    150 	return float64(s)
    151 }