You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
692 lines
19 KiB
692 lines
19 KiB
// UI around SwayRandr
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
"fmt"
|
|
"flag"
|
|
"strings"
|
|
"strconv"
|
|
"os/exec"
|
|
|
|
. "codeberg.org/snaums/swayrandr/lib"
|
|
|
|
"github.com/gotk3/gotk3/gtk"
|
|
"github.com/gotk3/gotk3/glib"
|
|
)
|
|
|
|
var Version string = ""; // Verion string; filled by Makefile
|
|
var BuildDate string = ""; // BuildDate; filled by Makefile
|
|
var BuildWith string = ""; // Compiler; filled by Makefile
|
|
|
|
var GladeString string = "";
|
|
|
|
var GtkBuilder *gtk.Builder
|
|
|
|
// structure with references to GTK-elements containing Information
|
|
// of a single monitor
|
|
type MonitorLine struct {
|
|
Frame *gtk.Frame // Frame element
|
|
NameLabel *gtk.Label // Name element (containing the connection name, like HDMI-1)
|
|
|
|
Name string
|
|
|
|
EnableBox *gtk.CheckButton // enable or disable the monitor
|
|
PrimaryBox *gtk.RadioButton
|
|
PositionBox *gtk.ComboBoxText // position the monitor (in a cross)
|
|
ResolutionBox *gtk.ComboBoxText // select resolution
|
|
RefreshBox *gtk.ComboBoxText // select refresh rate
|
|
ScaleBox *gtk.SpinButton
|
|
}
|
|
|
|
// structure with references to important objects in
|
|
// the main window
|
|
type MainWindow struct {
|
|
Window *gtk.ApplicationWindow // reference to the Window
|
|
ApplyBtn *gtk.Button // reference to the "Apply"-Button
|
|
CancelBtn *gtk.Button // reference to the "cancel"-Button
|
|
FileBtn *gtk.Button
|
|
RefreshBtn *gtk.Button
|
|
|
|
MonitorLabel *gtk.Label // label on the top with the monitor-count
|
|
MainBox *gtk.Box // main-content box; will be filled with the monitor lines
|
|
HdrBar *gtk.HeaderBar
|
|
|
|
Monitors []MonitorLine // list of lines of GTK-Elements; one for each monitor
|
|
RadioButtonGroup *glib.SList
|
|
|
|
ConfigFile string
|
|
}
|
|
|
|
var wnd *MainWindow
|
|
var screens []Screen
|
|
|
|
// fill in all modes available for a screen
|
|
func FillModes ( s Screen, box *gtk.ComboBoxText ) error {
|
|
var modelist []string;
|
|
for _, m := range s.Modes {
|
|
var entry string = strconv.Itoa(m.Width)+"x"+strconv.Itoa(m.Height)
|
|
var isIn bool = false
|
|
for _, v := range modelist {
|
|
if entry == v {
|
|
isIn = true
|
|
break;
|
|
}
|
|
}
|
|
|
|
if isIn == false {
|
|
modelist = append ( modelist, entry )
|
|
}
|
|
}
|
|
|
|
box.RemoveAll();
|
|
var isActive bool = false;
|
|
for k, m := range modelist {
|
|
box.Append ( strconv.Itoa(k), m )
|
|
|
|
var w, h int
|
|
xy := strings.Split ( m, "x" );
|
|
w, _ = strconv.Atoi ( xy[0] );
|
|
h, _ = strconv.Atoi ( xy[1] );
|
|
|
|
if s.CurrentMode.Width == w &&
|
|
s.CurrentMode.Height == h {
|
|
box.SetActive ( k );
|
|
isActive = true;
|
|
}
|
|
}
|
|
if isActive == false {
|
|
box.SetActive ( 0 );
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
// fill the refresh-rate box with its entries;
|
|
// depends on the currently selected mode
|
|
func FillRefreshRates ( s Screen, resolution string, box *gtk.ComboBoxText ) error {
|
|
var w, h int
|
|
if resolution == "" {
|
|
return nil;
|
|
}
|
|
xy := strings.Split ( resolution, "x" );
|
|
if len(xy) < 2 {
|
|
return nil;
|
|
}
|
|
w, _ = strconv.Atoi ( xy[0] );
|
|
h, _ = strconv.Atoi ( xy[1] );
|
|
|
|
var refreshList []string
|
|
for _, m := range s.Modes {
|
|
var rl string = strconv.FormatFloat ( float64( (float64(m.Refresh))/1000.0 ), 'f', 2, 64 )
|
|
if m.Width == w && m.Height == h {
|
|
var IsIn bool = false
|
|
for _, k := range refreshList {
|
|
if k == rl {
|
|
IsIn = true;
|
|
}
|
|
}
|
|
|
|
if IsIn == false {
|
|
refreshList = append ( refreshList, rl );
|
|
}
|
|
}
|
|
}
|
|
|
|
box.RemoveAll();
|
|
var current string = strconv.FormatFloat ( float64( (float64(s.CurrentMode.Refresh))/1000.0 ), 'f', 2, 64 )
|
|
var setactive bool = false;
|
|
for k, r := range refreshList {
|
|
box.AppendText ( r );
|
|
if current == r && w == s.CurrentMode.Width && h == s.CurrentMode.Height {
|
|
setactive = true;
|
|
box.SetActive ( k );
|
|
}
|
|
}
|
|
|
|
if setactive == false {
|
|
box.SetActive ( 0 );
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// prefill and preset the position-box with its entries
|
|
func FillPositions ( pos *gtk.ComboBoxText, preset string ) error {
|
|
positions := []string{ "left", "right", "above", "below", "middle" };
|
|
for k, p := range positions {
|
|
pos.AppendText ( p );
|
|
if preset == p || (preset == "" && p == "middle" ) {
|
|
pos.SetActive ( k );
|
|
}
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
func FillScale ( screen Screen, scale *gtk.SpinButton ) error {
|
|
scale.SetValue ( screen.Scale );
|
|
return nil;
|
|
}
|
|
|
|
// set sensitivity of the gtk-elements
|
|
func SetSensitive ( screen Screen, ml MonitorLine, active bool ) error {
|
|
ml.EnableBox.SetActive ( active );
|
|
ml.PrimaryBox.SetActive ( active );
|
|
ml.PositionBox.SetSensitive ( active );
|
|
ml.ResolutionBox.SetSensitive ( active );
|
|
ml.RefreshBox.SetSensitive ( active );
|
|
ml.ScaleBox.SetSensitive ( active );
|
|
|
|
return nil;
|
|
}
|
|
|
|
// for a single monitor: create a line of GTK-elements
|
|
// to display and alter information of the monitor
|
|
func CreateMonitorLine ( screen Screen, mainWnd *MainWindow, PosPreset string ) error {
|
|
frame, err := gtk.FrameNew ( screen.Make + " " + screen.Model )
|
|
if err != nil {
|
|
return err
|
|
}
|
|
box, err := gtk.BoxNew ( gtk.ORIENTATION_HORIZONTAL, 0 );
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nameLabel, err := gtk.LabelNew ( screen.Name );
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
EnableBox, err := gtk.CheckButtonNewWithLabel ( "Enable" );
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
PrimaryBox, err := gtk.RadioButtonNewWithLabel ( mainWnd.RadioButtonGroup, "Primary" );
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mainWnd.RadioButtonGroup, _ = PrimaryBox.GetGroup();
|
|
|
|
PosLabel, err := gtk.LabelNew ( "Position" );
|
|
if err != nil {
|
|
return err
|
|
}
|
|
PosBox, err := gtk.ComboBoxTextNew ();
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ResLabel, err := gtk.LabelNew ("Resolution" );
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ResBox, err := gtk.ComboBoxTextNew ();
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
RefreshLabel, err := gtk.LabelNew ("Refresh-Rate");
|
|
if err != nil {
|
|
return err
|
|
}
|
|
RefreshBox, err := gtk.ComboBoxTextNew ()
|
|
if err != nil {
|
|
return err;
|
|
}
|
|
|
|
ScaleLabel, err := gtk.LabelNew ("Scale");
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ScaleBox, err := gtk.SpinButtonNewWithRange ( 0, 50, 0.1)
|
|
if err != nil {
|
|
return err;
|
|
}
|
|
|
|
box.Add ( nameLabel );
|
|
box.Add ( EnableBox );
|
|
box.Add ( PrimaryBox );
|
|
box.Add ( PosLabel );
|
|
box.Add ( PosBox );
|
|
box.Add ( ResLabel );
|
|
box.Add ( ResBox );
|
|
box.Add ( RefreshLabel );
|
|
box.Add ( RefreshBox );
|
|
box.Add ( ScaleLabel );
|
|
box.Add ( ScaleBox );
|
|
|
|
frame.Add ( box );
|
|
frame.SetMarginTop ( 5 );
|
|
frame.SetMarginBottom ( 5 );
|
|
|
|
nameLabel.SetMarginStart ( 5 );
|
|
nameLabel.SetMarginEnd ( 10 );
|
|
EnableBox.SetMarginEnd ( 10 );
|
|
PrimaryBox.SetMarginEnd ( 10 );
|
|
PosLabel.SetMarginEnd ( 5 );
|
|
PosBox.SetMarginEnd ( 10 );
|
|
ResLabel.SetMarginEnd ( 5 );
|
|
ResBox.SetMarginEnd ( 10 );
|
|
RefreshLabel.SetMarginEnd ( 5 );
|
|
RefreshBox.SetMarginEnd ( 10 );
|
|
ScaleLabel.SetMarginEnd ( 5 );
|
|
ScaleBox.SetMarginEnd ( 5 );
|
|
|
|
frame.ShowAll();
|
|
|
|
mainWnd.MainBox.Add ( frame );
|
|
|
|
nm := MonitorLine{
|
|
Name: screen.Name,
|
|
Frame: frame,
|
|
NameLabel: nameLabel,
|
|
EnableBox: EnableBox,
|
|
PositionBox: PosBox,
|
|
ResolutionBox: ResBox,
|
|
RefreshBox: RefreshBox,
|
|
ScaleBox: ScaleBox,
|
|
PrimaryBox: PrimaryBox,
|
|
};
|
|
|
|
FillPositions ( nm.PositionBox, PosPreset );
|
|
FillModes ( screen, nm.ResolutionBox );
|
|
FillRefreshRates ( screen, nm.ResolutionBox.GetActiveText(), nm.RefreshBox );
|
|
FillScale ( screen, nm.ScaleBox );
|
|
|
|
SetSensitive ( screen, nm, screen.Active );
|
|
PrimaryBox.SetActive ( screen.Primary );
|
|
|
|
ResBox.Connect ("changed", func () {
|
|
FillRefreshRates ( screen, nm.ResolutionBox.GetActiveText(), nm.RefreshBox );
|
|
});
|
|
|
|
EnableBox.Connect ("toggled", func () {
|
|
SetSensitive ( screen, nm, nm.EnableBox.GetActive() );
|
|
});
|
|
|
|
wnd.Monitors = append ( wnd.Monitors, nm );
|
|
|
|
return nil;
|
|
}
|
|
|
|
// for all monitors: create a line with
|
|
// resolution, position and refresh-rate
|
|
func FillMonitorLines ( screens []Screen, mainWnd *MainWindow ) error {
|
|
wnd.MonitorLabel.SetLabel ("Found " + strconv.Itoa ( len(screens) ) + " Monitors");
|
|
for _, s := range screens {
|
|
err := CreateMonitorLine ( s, mainWnd, "" );
|
|
if err != nil {
|
|
return err;
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type MonitorConfig struct {
|
|
Screen Screen;
|
|
Enabled bool;
|
|
Primary bool;
|
|
Position string;
|
|
Resolution string;
|
|
Refresh string;
|
|
X,Y,Width,Height int;
|
|
RefreshValue int;
|
|
Scale float64;
|
|
ConfigFile string;
|
|
}
|
|
|
|
func Msg ( text string ) {
|
|
obj := gtk.MessageDialogNew (
|
|
wnd.Window,
|
|
gtk.DIALOG_DESTROY_WITH_PARENT,
|
|
gtk.MESSAGE_WARNING,
|
|
gtk.BUTTONS_OK,
|
|
text );
|
|
obj.SetDefaultResponse ( gtk.RESPONSE_OK );
|
|
obj.Connect ( "response", func () {
|
|
obj.Destroy();
|
|
});
|
|
obj.Run();
|
|
}
|
|
|
|
// Find necessary Objects in the window
|
|
// Fill the Window with the Monitor Information
|
|
// Set global Object-Signal Handlers
|
|
func PrepareMainWindow () *MainWindow {
|
|
if wnd != nil {
|
|
return wnd;
|
|
}
|
|
|
|
var err error
|
|
wnd = &MainWindow{}
|
|
|
|
obj, _ := GtkBuilder.GetObject ("MainWindow");
|
|
wnd.Window = obj.(*gtk.ApplicationWindow)
|
|
|
|
obj, _ = GtkBuilder.GetObject ( "MainBox" );
|
|
wnd.MainBox = obj.(*gtk.Box)
|
|
|
|
obj, _ = GtkBuilder.GetObject ("ApplyBtn");
|
|
wnd.ApplyBtn = obj.(*gtk.Button)
|
|
obj, _ = GtkBuilder.GetObject ("CancelBtn");
|
|
wnd.CancelBtn = obj.(*gtk.Button)
|
|
obj, _ = GtkBuilder.GetObject ("RefreshBtn");
|
|
wnd.RefreshBtn = obj.(*gtk.Button)
|
|
|
|
obj, _ = GtkBuilder.GetObject ("MonitorLabel");
|
|
wnd.MonitorLabel = obj.(*gtk.Label)
|
|
|
|
obj, _ = GtkBuilder.GetObject ("FileBtn");
|
|
wnd.FileBtn = obj.(*gtk.Button)
|
|
|
|
obj, _ = GtkBuilder.GetObject ("HdrBar");
|
|
wnd.HdrBar = obj.(*gtk.HeaderBar)
|
|
|
|
screens, err = ReadScreen ();
|
|
if err != nil {
|
|
return nil;
|
|
}
|
|
|
|
err = FillMonitorLines ( screens, wnd );
|
|
if err != nil {
|
|
fmt.Println ("Problems filling the Window: ", err.Error())
|
|
}
|
|
|
|
wnd.Window.Connect ("destroy", func() {
|
|
gtk.MainQuit()
|
|
});
|
|
|
|
wnd.CancelBtn.Connect ("clicked", func() {
|
|
wnd.Window.Close()
|
|
gtk.MainQuit()
|
|
});
|
|
|
|
wnd.RefreshBtn.Connect ("clicked", func() {
|
|
ReloadSway();
|
|
});
|
|
|
|
wnd.FileBtn.Connect ("clicked", func () {
|
|
fchoose, err := gtk.FileChooserNativeDialogNew (
|
|
"Choose the Monitor-Config file",
|
|
wnd.Window,
|
|
gtk.FILE_CHOOSER_ACTION_SAVE,
|
|
"Open",
|
|
"Cancel",
|
|
);
|
|
if err != nil {
|
|
Msg ( "Cannot create file chooser: " + err.Error() );
|
|
return
|
|
}
|
|
fchoose.SetSelectMultiple ( false );
|
|
ret := fchoose.Run ();
|
|
|
|
if ret == int(gtk.RESPONSE_ACCEPT) {
|
|
wnd.ConfigFile = fchoose.GetFilename();
|
|
wnd.HdrBar.SetSubtitle ( wnd.ConfigFile );
|
|
}
|
|
});
|
|
|
|
wnd.ApplyBtn.Connect ("clicked", func() {
|
|
screens, err := ReadScreen ();
|
|
if err != nil {
|
|
Msg ("Cannot read screen Information from sway");
|
|
return
|
|
}
|
|
|
|
// gather all information from the Window
|
|
var mc []MonitorConfig
|
|
for _, line := range wnd.Monitors {
|
|
var mx MonitorConfig
|
|
s := FindScreen ( screens, line.Name );
|
|
if s == nil {
|
|
Msg ("Cannot find the following screen: "+ line.Name );
|
|
return
|
|
}
|
|
|
|
mx.Screen = *s;
|
|
mx.Enabled = line.EnableBox.GetActive();
|
|
mx.Position = line.PositionBox.GetActiveText();
|
|
mx.Resolution = line.ResolutionBox.GetActiveText();
|
|
mx.Refresh = line.RefreshBox.GetActiveText();
|
|
mx.Scale = line.ScaleBox.GetValue();
|
|
mx.ConfigFile = wnd.ConfigFile;
|
|
|
|
if mx.Scale < 0 || mx.Scale > 50 {
|
|
Msg ("Scale out of bounds");
|
|
return;
|
|
}
|
|
|
|
mx.X = 0;
|
|
mx.Y = 0;
|
|
rv, err := strconv.ParseFloat ( mx.Refresh, 64 );
|
|
if err != nil {
|
|
Msg ("Cannot Parse float: "+mx.Refresh);
|
|
return
|
|
}
|
|
mx.RefreshValue = (int( rv * 1000 ))
|
|
|
|
xy := strings.Split ( mx.Resolution, "x" );
|
|
mx.Width, err = strconv.Atoi ( xy[0] );
|
|
if err != nil {
|
|
Msg ("Cannot Parse int for Width: "+xy[0]);
|
|
return
|
|
}
|
|
mx.Height, err = strconv.Atoi ( xy[1] );
|
|
if err != nil {
|
|
Msg ("Cannot Parse int for height: "+xy[1]);
|
|
return
|
|
}
|
|
|
|
mc = append( mc, mx );
|
|
}
|
|
|
|
// validate resolution selection
|
|
for k, _ := range mc {
|
|
m := &mc[k]
|
|
var IsIn bool = false;
|
|
for _, ms := range m.Screen.Modes {
|
|
if m.Width == ms.Width &&
|
|
m.Height == ms.Height {
|
|
if m.RefreshValue >= ms.Refresh-5 &&
|
|
m.RefreshValue <= ms.Refresh+5 {
|
|
m.RefreshValue = ms.Refresh;
|
|
IsIn = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if IsIn != true {
|
|
Msg ("Selected resolution is not available on screen (" + m.Screen.Name + ")");
|
|
return
|
|
}
|
|
}
|
|
|
|
// position the screens
|
|
Positioning ( &mc );
|
|
|
|
// run swayrandr once per monitor
|
|
for _, m := range mc {
|
|
var args []string = []string{ "--output", m.Screen.Name };
|
|
var run bool;
|
|
if m.Enabled == false {
|
|
args = append ( args, "--disable" );
|
|
if m.Screen.Active == true {
|
|
run = true;
|
|
}
|
|
} else {
|
|
args = append ( args, "--enable");
|
|
if m.Screen.Active == false {
|
|
run = true;
|
|
}
|
|
|
|
if m.Width != m.Screen.CurrentMode.Width ||
|
|
m.Height != m.Screen.CurrentMode.Height ||
|
|
m.RefreshValue != m.Screen.CurrentMode.Refresh {
|
|
args = append ( args, "--mode" );
|
|
args = append ( args, strconv.Itoa ( m.Width ) + "x" + strconv.Itoa( m.Height ) + "@" + strconv.FormatFloat ( float64(m.RefreshValue)/1000.0, 'f', -1, 64 ) );
|
|
run = true;
|
|
}
|
|
|
|
if m.X != m.Screen.Rect.X ||
|
|
m.Y != m.Screen.Rect.Y {
|
|
args = append ( args, "--position" );
|
|
args = append ( args, strconv.Itoa ( m.X ) + "+" + strconv.Itoa ( m.Y ) );
|
|
|
|
run = true;
|
|
}
|
|
|
|
if m.Scale != m.Screen.Scale {
|
|
args = append ( args, "--scale" );
|
|
args = append ( args, strconv.FormatFloat ( m.Scale, 'f', -1, 64 ));
|
|
run = true;
|
|
}
|
|
|
|
if m.ConfigFile != "" {
|
|
args = append ( args, "--config-file" );
|
|
args = append ( args, m.ConfigFile );
|
|
}
|
|
}
|
|
|
|
if run == true {
|
|
//fmt.Println ( args );
|
|
info, err := os.Stat ( "swayrandr" );
|
|
var program string;
|
|
if os.IsNotExist ( err ) || info.Mode().IsRegular () == false {
|
|
program = "swayrandr";
|
|
} else {
|
|
program = "./swayrandr";
|
|
}
|
|
|
|
out, err := exec.Command ( program, args... ).CombinedOutput ();
|
|
if err != nil {
|
|
Msg ("Cannot run swayrandr: " + err.Error() + "\nOutput: " + string(out) );
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return wnd;
|
|
}
|
|
|
|
// calculate X-Y Positions of the monitors by their selected
|
|
// positionBox-state
|
|
func Positioning ( mc *[]MonitorConfig ) {
|
|
// find the left-most monitor(s)
|
|
var currentX int = 0;
|
|
var nextX int = 0;
|
|
var adj_width int
|
|
var adj_height int
|
|
for i := 0; i < len(*mc); i++ {
|
|
var m *MonitorConfig = &(*mc)[i];
|
|
if m.Position == "left" {
|
|
m.X = 0;
|
|
adj_width = (m.Width*100/int(m.Scale*100))
|
|
if m.Width > nextX {
|
|
nextX = adj_width;
|
|
}
|
|
}
|
|
}
|
|
|
|
// middle monitors
|
|
currentX = nextX;
|
|
for i := 0; i < len(*mc); i++ {
|
|
var m *MonitorConfig = &(*mc)[i];
|
|
if m.Position == "middle" || m.Position == "above" || m.Position == "below" {
|
|
m.X = currentX;
|
|
adj_width = (m.Width*100/int(m.Scale*100))
|
|
if currentX + adj_width > nextX {
|
|
nextX = currentX + adj_width
|
|
}
|
|
}
|
|
}
|
|
|
|
// right monitors
|
|
currentX = nextX;
|
|
for i := 0; i < len(*mc); i++ {
|
|
var m *MonitorConfig = &(*mc)[i];
|
|
if m.Position == "right" {
|
|
m.X = currentX;
|
|
}
|
|
}
|
|
|
|
// Position the screens in the Y-Axis
|
|
var currentY int = 0;
|
|
var nextY int = 0;
|
|
for i := 0; i < len(*mc); i++ {
|
|
var m *MonitorConfig = &(*mc)[i];
|
|
if m.Position == "above" {
|
|
m.X = 0;
|
|
adj_height = (m.Height*100/int(m.Scale*100));
|
|
if m.Height > nextY {
|
|
nextY = adj_height
|
|
}
|
|
}
|
|
}
|
|
|
|
// middle monitors
|
|
currentY = nextY;
|
|
for i := 0; i < len(*mc); i++ {
|
|
var m *MonitorConfig = &(*mc)[i];
|
|
if m.Position == "middle" || m.Position == "left" || m.Position == "right" {
|
|
m.Y = currentY;
|
|
adj_height = (m.Height*100/int(m.Scale*100));
|
|
if currentY + m.Height > nextY {
|
|
nextY = currentY + adj_height;
|
|
}
|
|
}
|
|
}
|
|
|
|
// right monitors
|
|
currentY = nextY;
|
|
for i := 0; i < len(*mc); i++ {
|
|
var m *MonitorConfig = &(*mc)[i];
|
|
if m.Position == "below" {
|
|
m.Y = currentY;
|
|
}
|
|
}
|
|
}
|
|
|
|
// entry point into the LSwayRandr GUI
|
|
// builds the Windows from the glade-file and start the GTK-App
|
|
func main () {
|
|
var version bool
|
|
flag.BoolVar ( &version, "version", false, "Print version information" );
|
|
flag.Parse();
|
|
|
|
if version == true {
|
|
fmt.Println ("SwayRandr version:\t", Version );
|
|
fmt.Println (" Build on: \t", BuildDate );
|
|
fmt.Println (" Build with: \t", BuildWith );
|
|
fmt.Println ("Sway version: \t", SwayVersion() );
|
|
return;
|
|
}
|
|
|
|
gtk.Init ( nil );
|
|
var err error
|
|
GtkBuilder, err = gtk.BuilderNew();
|
|
if err != nil {
|
|
fmt.Println ("Builder error: ", err);
|
|
return;
|
|
}
|
|
|
|
info, err := os.Stat ( "ui.glade" );
|
|
if os.IsNotExist ( err ) || info.Mode().IsRegular() == false {
|
|
err = GtkBuilder.AddFromString ( GladeString );
|
|
} else {
|
|
// load window specification from glade-file
|
|
fmt.Println ("Loading glade description from file");
|
|
err = GtkBuilder.AddFromFile ("ui.glade");
|
|
}
|
|
if err != nil {
|
|
fmt.Println ("Builder parsing error: ", err);
|
|
return;
|
|
}
|
|
|
|
PrepareMainWindow ();
|
|
|
|
wnd.Window.Show();
|
|
gtk.Main()
|
|
}
|