Integrating fw-prompt into fw-settings

Updated fw-settings to reflect all recent changes
Moved rule notifications to dbus signal
Lots of various fixes
Local changes to gtk bindings
... And so much more...
shw-merge
xSmurf 7 years ago
parent 61710110d2
commit cfaf7b84ff

13
Godeps/Godeps.json generated

@ -1,7 +1,7 @@
{ {
"ImportPath": "github.com/subgraph/fw-daemon", "ImportPath": "github.com/subgraph/fw-daemon",
"GoVersion": "go1.7", "GoVersion": "go1.7",
"GodepVersion": "v79", "GodepVersion": "v74",
"Deps": [ "Deps": [
{ {
"ImportPath": "github.com/godbus/dbus", "ImportPath": "github.com/godbus/dbus",
@ -45,15 +45,15 @@
}, },
{ {
"ImportPath": "github.com/subgraph/go-nfnetlink", "ImportPath": "github.com/subgraph/go-nfnetlink",
"Rev": "34abd96bd88d0fbc1a5bbba9fff1bdc2c0448f47" "Rev": "bd5c281b400452a89af0cf298503f5d0bddc57c2"
}, },
{ {
"ImportPath": "github.com/subgraph/go-nfnetlink/nfqueue", "ImportPath": "github.com/subgraph/go-nfnetlink/nfqueue",
"Rev": "34abd96bd88d0fbc1a5bbba9fff1bdc2c0448f47" "Rev": "bd5c281b400452a89af0cf298503f5d0bddc57c2"
}, },
{ {
"ImportPath": "github.com/subgraph/go-procsnitch", "ImportPath": "github.com/subgraph/go-procsnitch",
"Rev": "fbc2965632eec2dcea9b8d630b081b10980d325d" "Rev": "26d0071b72fb28493634fff6b2194db40114f28a"
}, },
{ {
"ImportPath": "github.com/subgraph/oz/ipc", "ImportPath": "github.com/subgraph/oz/ipc",
@ -63,6 +63,11 @@
{ {
"ImportPath": "github.com/subgraph/ozipc", "ImportPath": "github.com/subgraph/ozipc",
"Rev": "2cbf2ba8878e9dea012edb021f3b4836f7c93202" "Rev": "2cbf2ba8878e9dea012edb021f3b4836f7c93202"
},
{
"ImportPath": "github.com/subgraph/oz/vendor/github.com/op/go-logging",
"Comment": "0.2.5",
"Rev": "69e556d2d33a76f19546471d813a21f7b7fa4181"
} }
] ]
} }

@ -2,6 +2,7 @@ package main
import ( import (
"bufio" "bufio"
"encoding/xml"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -18,6 +19,51 @@ const (
xmlExtension = ".xml" xmlExtension = ".xml"
) )
type GtkXMLInterface struct {
XMLName xml.Name `xml:"interface"`
Objects []*GtkXMLObject `xml:"object"`
Requires *GtkXMLRequires `xml:"requires"`
Comment string `xml:",comment"`
}
type GtkXMLRequires struct {
Lib string `xml:"lib,attr"`
Version string `xml:"version,attr"`
}
type GtkXMLObject struct {
XMLName xml.Name `xml:"object"`
Class string `xml:"class,attr"`
ID string `xml:"id,attr,omitempty"`
Properties []GtkXMLProperty `xml:"property"`
Children []GtkXMLChild `xml:"child,omitempty"`
Signals []GtkXMLSignal `xml:"signal,omitempty"`
}
type GtkXMLChild struct {
XMLName xml.Name `xml:"child"`
Objects []*GtkXMLObject `xml:"object"`
Placeholder *GtkXMLPlaceholder `xml:"placeholder,omitempty"`
InternalChild string `xml:"internal-child,attr,omitempty"`
}
type GtkXMLProperty struct {
XMLName xml.Name `xml:"property"`
Name string `xml:"name,attr"`
Translatable string `xml:"translatable,attr,omitempty"`
Value string `xml:",chardata"`
}
type GtkXMLSignal struct {
XMLName xml.Name `xml:"signal"`
Name string `xml:"name,attr"`
Handler string `xml:"handler,attr"`
}
type GtkXMLPlaceholder struct {
XMLName xml.Name `xml:"placeholder"`
}
func getDefinitionWithFileFallback(uiName string) string { func getDefinitionWithFileFallback(uiName string) string {
// this makes sure a missing definition wont break only when the app is released // this makes sure a missing definition wont break only when the app is released
uiDef := getDefinition(uiName) uiDef := getDefinition(uiName)
@ -30,6 +76,38 @@ func getDefinitionWithFileFallback(uiName string) string {
return readFile(fileName) return readFile(fileName)
} }
// This must be called from the UI thread - otherwise bad things will happen sooner or later
func builderForString(template string) *gtk.Builder {
// assertInUIThread()
maj := gtk.GetMajorVersion()
min := gtk.GetMinorVersion()
if (maj == 3) && (min < 20) {
fmt.Fprintf(os.Stderr,
"Attempting runtime work-around for older versions of libgtk-3...\n")
dep_re := regexp.MustCompile(`<\s?property\s+name\s?=\s?"icon_size"\s?>.+<\s?/property\s?>`)
template = dep_re.ReplaceAllString(template, ``)
dep_re2 := regexp.MustCompile(`version\s?=\s?"3.20"`)
template = dep_re2.ReplaceAllString(template, `version="3.18"`)
}
builder, err := gtk.BuilderNew()
if err != nil {
//We cant recover from this
panic(err)
}
err = builder.AddFromString(template)
if err != nil {
//This is a programming error
panic(fmt.Sprintf("gui: failed load string template: %s\n", err.Error()))
}
return builder
}
// This must be called from the UI thread - otherwise bad things will happen sooner or later // This must be called from the UI thread - otherwise bad things will happen sooner or later
func builderForDefinition(uiName string) *gtk.Builder { func builderForDefinition(uiName string) *gtk.Builder {
// assertInUIThread() // assertInUIThread()
@ -97,6 +175,10 @@ func newBuilder(uiName string) *builder {
return &builder{builderForDefinition(uiName)} return &builder{builderForDefinition(uiName)}
} }
func newBuilderFromString(template string) *builder {
return &builder{builderForString(template)}
}
func (b *builder) getItem(name string, target interface{}) { func (b *builder) getItem(name string, target interface{}) {
v := reflect.ValueOf(target) v := reflect.ValueOf(target)
if v.Kind() != reflect.Ptr { if v.Kind() != reflect.Ptr {

@ -1,29 +1,37 @@
package main package main
import ( import (
"github.com/gotk3/gotk3/gtk"
"github.com/subgraph/fw-daemon/sgfw" "github.com/subgraph/fw-daemon/sgfw"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
) )
func loadConfig(win *gtk.Window, b *builder, dbus *dbusObject) { type cbConfigChanged func()
var configCallbacks []cbConfigChanged
func (fa *fwApp) loadConfig(init bool) {
var levelCombo *gtk.ComboBoxText var levelCombo *gtk.ComboBoxText
var redactCheck *gtk.CheckButton var redactCheck *gtk.CheckButton
var expandedCheck *gtk.CheckButton var expandedCheck *gtk.CheckButton
var expertCheck *gtk.CheckButton var expertCheck *gtk.CheckButton
var actionCombo *gtk.ComboBoxText var actionCombo *gtk.ComboBoxText
var gridPrompt *gtk.Grid
var toplevelCheck *gtk.CheckButton
b.getItems( fa.winb.getItems(
"level_combo", &levelCombo, "level_combo", &levelCombo,
"redact_checkbox", &redactCheck, "redact_checkbox", &redactCheck,
"expanded_checkbox", &expandedCheck, "expanded_checkbox", &expandedCheck,
"expert_checkbox", &expertCheck, "expert_checkbox", &expertCheck,
"action_combo", &actionCombo, "action_combo", &actionCombo,
"grib_gtkprompt", &gridPrompt,
"toplevel_checkbox", &toplevelCheck,
) )
conf, err := dbus.getConfig() conf, err := fa.Dbus.getConfig()
if err != nil { if err != nil {
failDialog(win, "Failed to load config from fw daemon: %v", err) failDialog(&fa.win.Window, "Failed to load config from fw daemon: %v", err)
} }
if lvl, ok := conf["log_level"].(int32); ok { if lvl, ok := conf["log_level"].(int32); ok {
@ -35,32 +43,114 @@ func loadConfig(win *gtk.Window, b *builder, dbus *dbusObject) {
redactCheck.SetActive(v) redactCheck.SetActive(v)
} }
if v, ok := conf["prompt_expanded"].(bool); ok { if v, ok := conf["prompt_expanded"].(bool); ok {
fa.Config.PromptExpanded = v
expandedCheck.SetActive(v) expandedCheck.SetActive(v)
} }
if v, ok := conf["prompt_expert"].(bool); ok { if v, ok := conf["prompt_expert"].(bool); ok {
fa.Config.PromptExpert = v
expertCheck.SetActive(v) expertCheck.SetActive(v)
} }
if av, ok := conf["default_action"].(uint16); ok { if av, ok := conf["default_action"].(uint16); ok {
actionCombo.SetActiveID(sgfw.GetFilterScopeString(sgfw.FilterScope(av))) v := sgfw.GetFilterScopeString(sgfw.FilterScope(av))
fa.Config.DefaultAction = v
actionCombo.SetActiveID(v)
} }
b.ConnectSignals(map[string]interface{}{
"on_level_combo_changed": func() { if fa.promptMode == promptModeDisabled {
gridPrompt.SetNoShowAll(true)
gridPrompt.SetVisible(false)
expertCheck.SetTooltipText("")
} else {
l := expertCheck.GetChildren()
ect, _ := expertCheck.GetLabel()
ecl := gtk.Label{*l.NthData(0).(*gtk.Widget)}
ecl.SetUseMarkup(true)
ecl.SetMarkup("<s>" + ect + "</s>")
expertCheck.SetTooltipText("Applies only when using the GNOME Shell Prompter")
gridPrompt.SetNoShowAll(false)
gridPrompt.SetVisible(true)
toplevelCheck.SetActive(fa.Settings.GetToplevelPrompt())
}
if init {
levelCombo.Connect("changed", func() {
if lvl, ok := sgfw.IDToLevel[levelCombo.GetActiveID()]; ok { if lvl, ok := sgfw.IDToLevel[levelCombo.GetActiveID()]; ok {
dbus.setConfig("log_level", lvl) fa.Dbus.setConfig("log_level", lvl)
}
})
var redactHandler glib.SignalHandle
redactHandler, _ = redactCheck.Connect("toggled", func() {
val := redactCheck.GetActive()
if val {
fa.Dbus.setConfig("log_redact", val)
return
}
if fa.promptWarnLogRedact() {
fa.Dbus.setConfig("log_redact", val)
} else {
redactCheck.HandlerBlock(redactHandler)
redactCheck.SetActive(true)
redactCheck.HandlerUnblock(redactHandler)
} }
},
"on_redact_checkbox_toggled": func() {
dbus.setConfig("log_redact", redactCheck.GetActive())
},
"on_expanded_checkbox_toggled": func() {
dbus.setConfig("prompt_expanded", expandedCheck.GetActive())
},
"on_expert_checkbox_toggled": func() {
dbus.setConfig("prompt_expert", expertCheck.GetActive())
},
"on_action_combo_changed": func() {
dbus.setConfig("default_action", sgfw.GetFilterScopeValue(actionCombo.GetActiveID()))
},
}) })
expandedCheck.Connect("toggled", func() {
v := expandedCheck.GetActive()
fa.Config.PromptExpanded = v
fa.Dbus.setConfig("prompt_expanded", v)
fa.triggerConfigCallbacks()
})
expertCheck.Connect("toggled", func() {
v := expertCheck.GetActive()
fa.Config.PromptExpert = v
fa.Dbus.setConfig("prompt_expert", v)
fa.triggerConfigCallbacks()
})
actionCombo.Connect("changed", func() {
v := sgfw.GetFilterScopeValue(actionCombo.GetActiveID())
fa.Config.DefaultAction = string(sgfw.FilterScope(v))
fa.Dbus.setConfig("default_action", v)
fa.triggerConfigCallbacks()
})
if fa.promptMode != promptModeDisabled {
toplevelCheck.Connect("toggled", func() {
fa.Settings.SetToplevelPrompt(toplevelCheck.GetActive())
})
}
}
}
func (fa *fwApp) promptWarnLogRedact() bool {
res := false
body := "Are you sure you want to unredact logs?"
msg := "Sensitive information may get saved to the disk!"
d := gtk.MessageDialogNewWithMarkup(
fa.win,
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION,
gtk.BUTTONS_OK_CANCEL,
"")
d.SetMarkup(body)
d.SetProperty("secondary-text", msg)
if d.Run() == (int)(gtk.RESPONSE_OK) {
res = true
} else {
fa.win.SetUrgencyHint(false)
fa.win.SetKeepAbove(false)
}
d.Destroy()
return res
}
func (fa *fwApp) appendConfigCallback(fn cbConfigChanged) {
configCallbacks = append(configCallbacks, fn)
}
func (fa *fwApp) triggerConfigCallbacks() {
for _, fn := range configCallbacks {
glib.IdleAdd(func () bool {
fn()
return false
})
}
} }

@ -3,19 +3,21 @@ package main
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"strings"
"github.com/subgraph/fw-daemon/sgfw"
"github.com/godbus/dbus" "github.com/godbus/dbus"
"github.com/godbus/dbus/introspect"
"github.com/gotk3/gotk3/glib" "github.com/gotk3/gotk3/glib"
"github.com/subgraph/fw-daemon/sgfw"
) )
type dbusObject struct { type dbusObject struct {
dbus.BusObject dbus.BusObject
} }
type dbusObjectP struct {
dbus.BusObject
}
type dbusServer struct { type dbusServer struct {
conn *dbus.Conn conn *dbus.Conn
run bool run bool
@ -29,14 +31,6 @@ func newDbusObject() (*dbusObject, error) {
return &dbusObject{conn.Object("com.subgraph.Firewall", "/com/subgraph/Firewall")}, nil return &dbusObject{conn.Object("com.subgraph.Firewall", "/com/subgraph/Firewall")}, nil
} }
func newDbusObjectPrompt() (*dbusObjectP, error) {
conn, err := dbus.SystemBus()
if err != nil {
return nil, err
}
return &dbusObjectP{conn.Object("com.subgraph.fwprompt.EventNotifier", "/com/subgraph/fwprompt/EventNotifier")}, nil
}
func (ob *dbusObject) isEnabled() (bool, error) { func (ob *dbusObject) isEnabled() (bool, error) {
var flag bool var flag bool
if err := ob.Call("com.subgraph.Firewall.IsEnabled", 0).Store(&flag); err != nil { if err := ob.Call("com.subgraph.Firewall.IsEnabled", 0).Store(&flag); err != nil {
@ -55,11 +49,45 @@ func (ob *dbusObject) listRules() ([]sgfw.DbusRule, error) {
} }
func (ob *dbusObject) deleteRule(id uint32) { func (ob *dbusObject) deleteRule(id uint32) {
ob.Call("com.subgraph.Firewall.DeleteRule", 0, id) fmt.Printf("Deleting rule: %d\n", id)
res := ob.Call("com.subgraph.Firewall.DeleteRule", 0, id)
if res.Err != nil {
fmt.Printf("DBUS Delete error with %+v\n", res.Err)
}
} }
func (ob *dbusObject) updateRule(rule *sgfw.DbusRule) { func (ob *dbusObject) updateRule(rule *sgfw.DbusRule) {
ob.Call("com.subgraph.Firewall.UpdateRule", 0, rule) fmt.Printf("Updating rule: %+v\n", rule)
res := ob.Call("com.subgraph.Firewall.UpdateRule", 0, rule)
if res.Err != nil {
fmt.Printf("DBUS UPdate error with %+v\n", res.Err)
}
}
func (ob *dbusObject) answerPrompt(scope uint32, rule, guid string) error {
var dres bool
call := ob.Call("AddRuleAsync", 0, uint32(scope), rule, "*", guid)
err := call.Store(&dres)
if err != nil {
fmt.Printf("Error notifying SGFW of asynchronous rule addition: %+v\n", err)
return err
}
fmt.Println("makeDecision remote result:", dres)
return nil
}
func (ob *dbusObject) addRule(rule *sgfw.DbusRule) (bool, error) {
var dres bool
fmt.Printf("Adding new rule: %+v\n", rule)
call := ob.Call("com.subgraph.Firewall.AddNewRule", 0, rule)
err := call.Store(&dres)
if err != nil {
fmt.Println("Error while adding new rule:", err)
return false, err
}
return dres, nil
} }
func (ob *dbusObject) getConfig() (map[string]interface{}, error) { func (ob *dbusObject) getConfig() (map[string]interface{}, error) {
@ -78,14 +106,109 @@ func (ob *dbusObject) setConfig(key string, val interface{}) {
ob.Call("com.subgraph.Firewall.SetConfig", 0, key, dbus.MakeVariant(val)) ob.Call("com.subgraph.Firewall.SetConfig", 0, key, dbus.MakeVariant(val))
} }
func newDbusServer() (*dbusServer, error) { func dbusSignalHandler(app *fwApp) {
for {
conn, err := dbus.SystemBus()
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to connect to bus: ", err)
}
defer conn.Close()
conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
"type='signal',path='/com/subgraph/Firewall',interface='com.subgraph.Firewall',sender='com.subgraph.Firewall'")
c := make(chan *dbus.Signal, 10)
conn.Signal(c)
for v := range c {
if !strings.HasPrefix(v.Name, "com.subgraph.Firewall.") {
continue
}
if len(v.Body) == 0 {
continue
}
val := v.Body[0].(string)
name := strings.ToLower(strings.Replace(v.Name, "com.subgraph.Firewall.", "", 1))
fmt.Printf("Received Dbus update alert: %s(%v)\n", name, val)
switch name {
case "refresh":
switch val {
case "init":
glib.IdleAdd(func () bool {
if app.promptMode != promptModeDisabled {
app.prompt.RemoveAll()
}
app.handleRefreshRules()
app.handleRefreshConfig()
return false
})
case "rules":
glib.IdleAdd(func () bool {
app.handleRefreshRules()
return false
})
case "config":
glib.IdleAdd(func () bool {
app.handleRefreshConfig()
return false
})
default:
continue
}
default:
continue
}
}
}
}
/*
* DBus Prompt Service
*/
const introspectPromptXML = `
<node>
<interface name="com.subgraph.FirewallPrompt">
<method name="RequestPromptAsync">
<arg type="s" direction="in" name="guid" />
<arg type="s" direction="in" name="application" />
<arg type="s" direction="in" name="icon" />
<arg type="s" direction="in" name="path" />
<arg type="s" direction="in" name="address" />
<arg type="i" direction="in" name="port" />
<arg type="s" direction="in" name="ip" />
<arg type="s" direction="in" name="origin" />
<arg type="s" direction="in" name="proto" />
<arg type="i" direction="in" name="uid" />
<arg type="i" direction="in" name="gid" />
<arg type="s" direction="in" name="user" />
<arg type="s" direction="in" name="group" />
<arg type="i" direction="in" name="pid" />
<arg type="s" direction="in" name="sandbox" />
<arg type="b" direction="in" name="tlsguard" />
<arg type="s" direction="in" name="timestamp" />
<arg type="s" direction="in" name="optstring" />
<arg type="b" direction="in" name="expanded" />
<arg type="b" direction="in" name="expert" />
<arg type="i" direction="in" name="action" />
<arg type="b" direction="out" name="result" />
</method>
<method name="RemovePrompt">
<arg type="s" direction="in" name="guid" />
<arg type="b" direction="out" name="result" />
</method>
</interface>` +
introspect.IntrospectDataString +
`</node>`
func newPromptDbusServer() (*dbusServer, error) {
conn, err := dbus.SystemBus() conn, err := dbus.SystemBus()
if err != nil { if err != nil {
return nil, err return nil, err
} }
reply, err := conn.RequestName("com.subgraph.fwprompt.EventNotifier", dbus.NameFlagDoNotQueue) reply, err := conn.RequestName("com.subgraph.FirewallPrompt", dbus.NameFlagDoNotQueue)
if err != nil { if err != nil {
return nil, err return nil, err
@ -97,21 +220,39 @@ func newDbusServer() (*dbusServer, error) {
ds := &dbusServer{} ds := &dbusServer{}
if err := conn.Export(ds, "/com/subgraph/fwprompt/EventNotifier", "com.subgraph.fwprompt.EventNotifier"); err != nil { if err := conn.Export(ds, "/com/subgraph/FirewallPrompt", "com.subgraph.FirewallPrompt"); err != nil {
return nil, err
}
if err := conn.Export(introspect.Introspectable(introspectPromptXML), "/com/subgraph/FirewallPrompt", "org.freedesktop.DBus.Introspectable"); err != nil {
return nil, err return nil, err
} }
ds.conn = conn ds.conn = conn
ds.run = true ds.run = true
return ds, nil return ds, nil
} }
func (ds *dbusServer) Alert(data string) *dbus.Error { func (ds *dbusServer) RequestPromptAsync(guid, application, icon, path, address string, port int32, ip, origin, proto string, uid, gid int32, username, groupname string, pid int32, sandbox string,
fmt.Println("Received Dbus update alert: ", data) is_socks bool, timestamp string, optstring string, expanded, expert bool, action int32) (bool, *dbus.Error) {
glib.IdleAdd(repopulateWin) fmt.Printf("ASYNC request prompt: guid = %s, app = %s, icon = %s, path = %s, address = %s / ip = %s, is_socks = %v, sandbox = %v, action = %v\n", guid, application, icon, path, address, ip, is_socks, sandbox, action)
return nil if cbPromptAddRequest != nil {
glib.IdleAdd(func () bool {
cbPromptAddRequest(guid, path, icon, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, timestamp, is_socks, optstring, sandbox, int(action))
return false
})
}
return true, nil
} }
func (ob *dbusObjectP) alertRule(data string) { func (ds *dbusServer) RemovePrompt(guid string) *dbus.Error {
ob.Call("com.subgraph.fwprompt.EventNotifier.Alert", 0, data) fmt.Printf("++++++++ Cancelling prompt: %s\n", guid)
if cbPromptRemoveRequest != nil {
glib.IdleAdd(func () bool {
cbPromptRemoveRequest(guid)
return false
})
}
return nil
} }

@ -2,12 +2,22 @@
<!-- Generated with glade 3.20.0 --> <!-- Generated with glade 3.20.0 -->
<interface> <interface>
<requires lib="gtk+" version="3.20"/> <requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="window"> <object class="GtkImage" id="img_btn_add">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">document-new-symbolic</property>
</object>
<object class="GtkImage" id="img_btn_search">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-find-symbolic</property>
</object>
<object class="GtkApplicationWindow" id="window">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="title">Subgraph Firewall</property> <property name="title">Subgraph Firewall</property>
<property name="window_position">center</property> <property name="window_position">center</property>
<property name="default_width">600</property> <property name="default_width">700</property>
<property name="default_height">400</property> <property name="default_height">600</property>
<child> <child>
<object class="GtkBox" id="box1"> <object class="GtkBox" id="box1">
<property name="can_focus">False</property> <property name="can_focus">False</property>
@ -18,7 +28,56 @@
<object class="GtkStack" id="toplevel_stack"> <object class="GtkStack" id="toplevel_stack">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_bottom">5</property> <property name="margin_bottom">5</property>
<property name="transition_duration">1000</property> <property name="transition_duration">250</property>
<property name="transition_type">slide-left-right</property>
<child>
<object class="GtkScrolledWindow" id="prompt_scrollwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="name">prompt</property>
<property name="title" translatable="yes">Prompt</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkRevealer" id="search_revealer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="transition_type">slide-up</property>
<child>
<object class="GtkSearchEntry" id="search_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Rule path or sandbox</property>
<property name="halign">center</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="width_chars">48</property>
<property name="truncate_multiline">True</property>
<property name="caps_lock_warning">False</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<property name="placeholder_text" translatable="yes">Rule path or sandbox</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child> <child>
<object class="GtkNotebook" id="rulesnotebook"> <object class="GtkNotebook" id="rulesnotebook">
<property name="visible">True</property> <property name="visible">True</property>
@ -127,14 +186,60 @@
<property name="tab_fill">False</property> <property name="tab_fill">False</property>
</packing> </packing>
</child> </child>
<child type="action-start">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">2</property>
<property name="margin_bottom">2</property>
<child>
<object class="GtkToggleButton" id="btn_search">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">img_btn_search</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="name">page0</property> <property name="name">rules</property>
<property name="title" translatable="yes">Rules</property> <property name="title" translatable="yes">Rules</property>
<property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkGrid" id="grid1"> <object class="GtkGrid" id="config_grid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_left">10</property> <property name="margin_left">10</property>
@ -208,7 +313,6 @@
<item id="info" translatable="yes">Info</item> <item id="info" translatable="yes">Info</item>
<item id="debug" translatable="yes">Debug</item> <item id="debug" translatable="yes">Debug</item>
</items> </items>
<signal name="changed" handler="on_level_combo_changed" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
@ -224,7 +328,6 @@
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">10</property> <property name="margin_left">10</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_redact_checkbox_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
@ -241,7 +344,6 @@
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">10</property> <property name="margin_left">10</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_expanded_checkbox_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
@ -258,7 +360,6 @@
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">10</property> <property name="margin_left">10</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_expert_checkbox_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
@ -276,7 +377,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">6</property> <property name="top_attach">7</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -288,20 +389,49 @@
<items> <items>
<item id="FOREVER" translatable="yes">Forever</item> <item id="FOREVER" translatable="yes">Forever</item>
<item id="SESSION" translatable="yes">Session</item> <item id="SESSION" translatable="yes">Session</item>
<item id="PROCESS" translatable="yes">Process</item>
<item id="ONCE" translatable="yes">Once</item> <item id="ONCE" translatable="yes">Once</item>
</items> </items>
<signal name="changed" handler="on_action_combo_changed" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grib_gtkprompt">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">5</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkCheckButton" id="toplevel_checkbox">
<property name="label" translatable="yes">Bring window front most and make sticky on new prompt events</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">start</property>
<property name="margin_left">10</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property> <property name="top_attach">6</property>
<property name="width">2</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="name">page1</property> <property name="name">config</property>
<property name="title" translatable="yes">Options</property> <property name="title" translatable="yes">Options</property>
<property name="position">1</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
</object> </object>
@ -318,12 +448,83 @@
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="show_close_button">True</property> <property name="show_close_button">True</property>
<property name="decoration_layout">:minimize,maximize,close</property> <property name="decoration_layout">:minimize,maximize,close</property>
<child>
<object class="GtkBox" id="box_app_menu">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">center</property>
<child>
<object class="GtkMenuButton" id="btn_app_menu">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage" id="gear_image">
<property name="can_focus">False</property>
<property name="icon_name">open-menu-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
<style>
<class name="image-button"/>
<class name="appmenu"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="btn_new_rule">
<property name="visible">True</property>
<property name="app_paintable">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Add New Rule</property>
<property name="valign">center</property>
<property name="hexpand">False</property>
<property name="vexpand">False</property>
<property name="image">img_btn_add</property>
<signal name="clicked" handler="on_btn_new_rule_clicked" swapped="no"/>
<style>
<class name="image-button"/>
<class name="suggested-action"/>
<class name="default"/>
</style>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="title"> <child type="title">
<object class="GtkStackSwitcher" id="stack_switcher"> <object class="GtkStackSwitcher" id="stack_switcher">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="valign">center</property>
<property name="icon_size">2</property> <property name="icon_size">2</property>
<property name="stack">toplevel_stack</property> <property name="stack">toplevel_stack</property>
</object> </object>
<packing>
<property name="position">2</property>
</packing>
</child> </child>
</object> </object>
</child> </child>

@ -6,13 +6,12 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="row_homogeneous">True</property>
<child> <child>
<object class="GtkLabel" id="app_label"> <object class="GtkImage" id="app_icon">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="stock">gtk-missing-image</property>
<property name="margin_left">8</property>
<property name="margin_right">10</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
@ -20,11 +19,13 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="verb_label"> <object class="GtkLabel" id="app_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">8</property>
<property name="margin_right">10</property> <property name="margin_right">10</property>
<property name="xalign">0</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
@ -32,39 +33,28 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="origin_label"> <object class="GtkImage" id="verb_icon">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="stock">gtk-no</property>
<property name="margin_right">10</property>
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="top_attach">0</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkLabel" id="privs_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_right">10</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<child> <child>
<object class="GtkLabel" id="target_label"> <object class="GtkLabel" id="target_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_right">10</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="xpad">10</property>
<property name="xalign">0</property>
</object> </object>
<packing> <packing>
<property name="left_attach">4</property> <property name="left_attach">3</property>
<property name="top_attach">0</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
@ -85,45 +75,45 @@
</child> </child>
</object> </object>
<packing> <packing>
<property name="left_attach">5</property> <property name="left_attach">4</property>
<property name="top_attach">0</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="delete_button"> <object class="GtkButton" id="save_button">
<property name="visible">True</property> <property name="sensitive">False</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Delete firewall rule</property> <property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">Save as new permanent firewall rule</property>
<property name="relief">none</property> <property name="relief">none</property>
<signal name="clicked" handler="on_delete_rule" swapped="no"/> <signal name="clicked" handler="on_save_rule" swapped="no"/>
<child> <child>
<object class="GtkImage" id="image1"> <object class="GtkImage" id="img_save_button">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="icon_name">edit-delete-symbolic</property> <property name="icon_name">document-new-symbolic</property>
</object> </object>
</child> </child>
</object> </object>
<packing> <packing>
<property name="left_attach">7</property> <property name="left_attach">5</property>
<property name="top_attach">0</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="save_button"> <object class="GtkButton" id="delete_button">
<property name="sensitive">False</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="no_show_all">True</property> <property name="tooltip_text" translatable="yes">Delete firewall rule</property>
<property name="tooltip_text" translatable="yes">Save as new permanent firewall rule</property>
<property name="relief">none</property> <property name="relief">none</property>
<signal name="clicked" handler="on_save_rule" swapped="no"/> <signal name="clicked" handler="on_delete_rule" swapped="no"/>
<child> <child>
<object class="GtkImage" id="img_save_button"> <object class="GtkImage" id="image1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="icon_name">document-new-symbolic</property> <property name="icon_name">edit-delete-symbolic</property>
</object> </object>
</child> </child>
</object> </object>

@ -0,0 +1,573 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<object class="GtkDialog" id="dialog">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Edit Rule</property>
<property name="role">SubgraphFirewallNewRule</property>
<property name="resizable">False</property>
<property name="modal">True</property>
<property name="window_position">center</property>
<property name="destroy_with_parent">True</property>
<property name="icon_name">alacarte</property>
<property name="type_hint">dialog</property>
<property name="skip_taskbar_hint">True</property>
<property name="deletable">False</property>
<property name="startup_id">SubgraphFirewallNewRule</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="action_btnbox">
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="homogeneous">True</property>
<property name="layout_style">spread</property>
<child>
<object class="GtkButton" id="allow_button">
<property name="label" translatable="yes">_Allow</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ok_button">
<property name="label" translatable="yes">_Ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="verb_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<items>
<item id="allow" translatable="yes">Allow</item>
<item id="deny" translatable="yes">Deny</item>
</items>
<signal name="changed" handler="on_verb_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">5</property>
<property name="column_spacing">10</property>
<property name="row_homogeneous">True</property>
<child>
<object class="GtkLabel" id="path_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Path:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="sandbox_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Sandbox:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="uid_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">UID:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="gid_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">GID:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">8</property>
</packing>
</child>
<child>
<object class="GtkFileChooserButton" id="path_chooser">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="create_folders">False</property>
<property name="preview_widget_active">False</property>
<property name="use_preview_label">False</property>
<property name="title" translatable="yes">Select Executable Path</property>
<signal name="file-set" handler="on_path_changed" swapped="no"/>
<signal name="file-set" handler="on_path_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="scope_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Scope:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">9</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="sandbox_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="proto_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Protocol:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="host_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Host:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="host_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="width_chars">64</property>
<property name="max_width_chars">34</property>
<property name="primary_icon_tooltip_markup" translatable="yes" context="The character * can be use to match any value.">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="placeholder_text" translatable="yes">Hostname or IP address</property>
<signal name="changed" handler="on_host_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="port_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="max_length">5</property>
<property name="width_chars">4</property>
<property name="max_width_chars">5</property>
<property name="primary_icon_tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="placeholder_text" translatable="yes">Port</property>
<signal name="changed" handler="on_port_changed" swapped="no"/>
<signal name="insert-text" handler="on_port_insert_text" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="port_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Port:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="sandbox_label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">SANDBOX</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="pid_title">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Pid:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="pid_label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">PID_LABEL</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="scope_label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">SCOPE_LABEL</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">9</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="homogeneous">True</property>
<property name="baseline_position">top</property>
<child>
<object class="GtkComboBoxText" id="uid_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<property name="active_id">-1</property>
<items>
<item id="-1">Any User</item>
</items>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="uid_checkbox">
<property name="label" translatable="yes">Apply</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkComboBoxText" id="gid_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<property name="active_id">-1</property>
<items>
<item id="-1">Any Group</item>
</items>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="gid_checkbox">
<property name="label" translatable="yes">Apply</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">8</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="path_entry">
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="origin_label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Origin:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="origin_entry">
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="scope_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<property name="active_id">0</property>
<items>
<item id="2" translatable="yes">Permanent</item>
<item id="0" translatable="yes">Session</item>
<item id="1" translatable="yes">Process</item>
<item id="4" translatable="yes">Once</item>
<item id="3" translatable="yes">System</item>
</items>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">9</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkComboBoxText" id="proto_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">1</property>
<property name="active_id">1</property>
<items>
<item translatable="yes">Any</item>
<item id="tcp" translatable="yes">TCP</item>
<item id="udp" translatable="yes">UDP</item>
<item id="icmp" translatable="yes">ICMP</item>
</items>
<signal name="changed" handler="on_proto_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="tls_check">
<property name="label" translatable="yes">TLS Only</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="3">allow_button</action-widget>
<action-widget response="1">cancel_button</action-widget>
<action-widget response="2">ok_button</action-widget>
</action-widgets>
</object>
</interface>

@ -12,12 +12,22 @@ func (*defDialog) String() string {
<!-- Generated with glade 3.20.0 --> <!-- Generated with glade 3.20.0 -->
<interface> <interface>
<requires lib="gtk+" version="3.20"/> <requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="window"> <object class="GtkImage" id="img_btn_add">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">document-new-symbolic</property>
</object>
<object class="GtkImage" id="img_btn_search">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-find-symbolic</property>
</object>
<object class="GtkApplicationWindow" id="window">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="title">Subgraph Firewall</property> <property name="title">Subgraph Firewall</property>
<property name="window_position">center</property> <property name="window_position">center</property>
<property name="default_width">600</property> <property name="default_width">700</property>
<property name="default_height">400</property> <property name="default_height">600</property>
<child> <child>
<object class="GtkBox" id="box1"> <object class="GtkBox" id="box1">
<property name="can_focus">False</property> <property name="can_focus">False</property>
@ -28,7 +38,56 @@ func (*defDialog) String() string {
<object class="GtkStack" id="toplevel_stack"> <object class="GtkStack" id="toplevel_stack">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_bottom">5</property> <property name="margin_bottom">5</property>
<property name="transition_duration">1000</property> <property name="transition_duration">250</property>
<property name="transition_type">slide-left-right</property>
<child>
<object class="GtkScrolledWindow" id="prompt_scrollwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="name">prompt</property>
<property name="title" translatable="yes">Prompt</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkRevealer" id="search_revealer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="transition_type">slide-up</property>
<child>
<object class="GtkSearchEntry" id="search_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Rule path or sandbox</property>
<property name="halign">center</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="width_chars">48</property>
<property name="truncate_multiline">True</property>
<property name="caps_lock_warning">False</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<property name="placeholder_text" translatable="yes">Rule path or sandbox</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child> <child>
<object class="GtkNotebook" id="rulesnotebook"> <object class="GtkNotebook" id="rulesnotebook">
<property name="visible">True</property> <property name="visible">True</property>
@ -137,14 +196,60 @@ func (*defDialog) String() string {
<property name="tab_fill">False</property> <property name="tab_fill">False</property>
</packing> </packing>
</child> </child>
<child type="action-start">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">2</property>
<property name="margin_bottom">2</property>
<child>
<object class="GtkToggleButton" id="btn_search">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">img_btn_search</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="name">page0</property> <property name="name">rules</property>
<property name="title" translatable="yes">Rules</property> <property name="title" translatable="yes">Rules</property>
<property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkGrid" id="grid1"> <object class="GtkGrid" id="config_grid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_left">10</property> <property name="margin_left">10</property>
@ -218,7 +323,6 @@ func (*defDialog) String() string {
<item id="info" translatable="yes">Info</item> <item id="info" translatable="yes">Info</item>
<item id="debug" translatable="yes">Debug</item> <item id="debug" translatable="yes">Debug</item>
</items> </items>
<signal name="changed" handler="on_level_combo_changed" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
@ -234,7 +338,6 @@ func (*defDialog) String() string {
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">10</property> <property name="margin_left">10</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_redact_checkbox_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
@ -251,7 +354,6 @@ func (*defDialog) String() string {
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">10</property> <property name="margin_left">10</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_expanded_checkbox_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
@ -268,7 +370,6 @@ func (*defDialog) String() string {
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">10</property> <property name="margin_left">10</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_expert_checkbox_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
@ -286,7 +387,7 @@ func (*defDialog) String() string {
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">6</property> <property name="top_attach">7</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -301,18 +402,46 @@ func (*defDialog) String() string {
<item id="PROCESS" translatable="yes">Process</item> <item id="PROCESS" translatable="yes">Process</item>
<item id="ONCE" translatable="yes">Once</item> <item id="ONCE" translatable="yes">Once</item>
</items> </items>
<signal name="changed" handler="on_action_combo_changed" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grib_gtkprompt">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">5</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkCheckButton" id="toplevel_checkbox">
<property name="label" translatable="yes">Bring window front most and make sticky on new prompt events</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">start</property>
<property name="margin_left">10</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property> <property name="top_attach">6</property>
<property name="width">2</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="name">page1</property> <property name="name">config</property>
<property name="title" translatable="yes">Options</property> <property name="title" translatable="yes">Options</property>
<property name="position">1</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
</object> </object>
@ -329,12 +458,83 @@ func (*defDialog) String() string {
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="show_close_button">True</property> <property name="show_close_button">True</property>
<property name="decoration_layout">:minimize,maximize,close</property> <property name="decoration_layout">:minimize,maximize,close</property>
<child>
<object class="GtkBox" id="box_app_menu">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="valign">center</property>
<child>
<object class="GtkMenuButton" id="btn_app_menu">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage" id="gear_image">
<property name="can_focus">False</property>
<property name="icon_name">open-menu-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
<style>
<class name="image-button"/>
<class name="appmenu"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="btn_new_rule">
<property name="visible">True</property>
<property name="app_paintable">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Add New Rule</property>
<property name="valign">center</property>
<property name="hexpand">False</property>
<property name="vexpand">False</property>
<property name="image">img_btn_add</property>
<signal name="clicked" handler="on_btn_new_rule_clicked" swapped="no"/>
<style>
<class name="image-button"/>
<class name="suggested-action"/>
<class name="default"/>
</style>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="title"> <child type="title">
<object class="GtkStackSwitcher" id="stack_switcher"> <object class="GtkStackSwitcher" id="stack_switcher">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="valign">center</property>
<property name="icon_size">2</property> <property name="icon_size">2</property>
<property name="stack">toplevel_stack</property> <property name="stack">toplevel_stack</property>
</object> </object>
<packing>
<property name="position">2</property>
</packing>
</child> </child>
</object> </object>
</child> </child>

@ -16,13 +16,12 @@ func (*defRuleItem) String() string {
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="row_homogeneous">True</property>
<child> <child>
<object class="GtkLabel" id="app_label"> <object class="GtkImage" id="app_icon">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="stock">gtk-missing-image</property>
<property name="margin_left">8</property>
<property name="margin_right">10</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
@ -30,11 +29,13 @@ func (*defRuleItem) String() string {
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="verb_label"> <object class="GtkLabel" id="app_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">8</property>
<property name="margin_right">10</property> <property name="margin_right">10</property>
<property name="xalign">0</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
@ -42,38 +43,28 @@ func (*defRuleItem) String() string {
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="origin_label"> <object class="GtkImage" id="verb_icon">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="stock">gtk-no</property>
<property name="margin_right">10</property>
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="top_attach">0</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkLabel" id="privs_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_right">10</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child> <child>
<object class="GtkLabel" id="target_label"> <object class="GtkLabel" id="target_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_right">10</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="xpad">10</property>
<property name="xalign">0</property>
</object> </object>
<packing> <packing>
<property name="left_attach">4</property> <property name="left_attach">3</property>
<property name="top_attach">0</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
@ -94,45 +85,45 @@ func (*defRuleItem) String() string {
</child> </child>
</object> </object>
<packing> <packing>
<property name="left_attach">5</property> <property name="left_attach">4</property>
<property name="top_attach">0</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="delete_button"> <object class="GtkButton" id="save_button">
<property name="visible">True</property> <property name="sensitive">False</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Delete firewall rule</property> <property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">Save as new permanent firewall rule</property>
<property name="relief">none</property> <property name="relief">none</property>
<signal name="clicked" handler="on_delete_rule" swapped="no"/> <signal name="clicked" handler="on_save_rule" swapped="no"/>
<child> <child>
<object class="GtkImage" id="image1"> <object class="GtkImage" id="img_save_button">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="icon_name">edit-delete-symbolic</property> <property name="icon_name">document-new-symbolic</property>
</object> </object>
</child> </child>
</object> </object>
<packing> <packing>
<property name="left_attach">7</property> <property name="left_attach">5</property>
<property name="top_attach">0</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="save_button"> <object class="GtkButton" id="delete_button">
<property name="sensitive">False</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="no_show_all">True</property> <property name="tooltip_text" translatable="yes">Delete firewall rule</property>
<property name="tooltip_text" translatable="yes">Save as new permanent firewall rule</property>
<property name="relief">none</property> <property name="relief">none</property>
<signal name="clicked" handler="on_save_rule" swapped="no"/> <signal name="clicked" handler="on_delete_rule" swapped="no"/>
<child> <child>
<object class="GtkImage" id="img_save_button"> <object class="GtkImage" id="image1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="icon_name">document-new-symbolic</property> <property name="icon_name">edit-delete-symbolic</property>
</object> </object>
</child> </child>
</object> </object>

@ -0,0 +1,586 @@
package definitions
func init() {
add(`RuleNew`, &defRuleNew{})
}
type defRuleNew struct{}
func (*defRuleNew) String() string {
return `
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<object class="GtkDialog" id="dialog">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Edit Rule</property>
<property name="role">SubgraphFirewallNewRule</property>
<property name="resizable">False</property>
<property name="modal">True</property>
<property name="window_position">center</property>
<property name="destroy_with_parent">True</property>
<property name="icon_name">alacarte</property>
<property name="type_hint">dialog</property>
<property name="skip_taskbar_hint">True</property>
<property name="deletable">False</property>
<property name="startup_id">SubgraphFirewallNewRule</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="action_btnbox">
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="homogeneous">True</property>
<property name="layout_style">spread</property>
<child>
<object class="GtkButton" id="allow_button">
<property name="label" translatable="yes">_Allow</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ok_button">
<property name="label" translatable="yes">_Ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="verb_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<items>
<item id="allow" translatable="yes">Allow</item>
<item id="deny" translatable="yes">Deny</item>
</items>
<signal name="changed" handler="on_verb_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">5</property>
<property name="column_spacing">10</property>
<property name="row_homogeneous">True</property>
<child>
<object class="GtkLabel" id="path_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Path:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="sandbox_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Sandbox:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="uid_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">UID:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="gid_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">GID:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">8</property>
</packing>
</child>
<child>
<object class="GtkFileChooserButton" id="path_chooser">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="create_folders">False</property>
<property name="preview_widget_active">False</property>
<property name="use_preview_label">False</property>
<property name="title" translatable="yes">Select Executable Path</property>
<signal name="file-set" handler="on_path_changed" swapped="no"/>
<signal name="file-set" handler="on_path_set" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="scope_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Scope:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">9</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="sandbox_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="proto_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Protocol:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="host_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Host:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="host_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="width_chars">64</property>
<property name="max_width_chars">34</property>
<property name="primary_icon_tooltip_markup" translatable="yes" context="The character * can be use to match any value.">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="placeholder_text" translatable="yes">Hostname or IP address</property>
<signal name="changed" handler="on_host_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="port_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="max_length">5</property>
<property name="width_chars">4</property>
<property name="max_width_chars">5</property>
<property name="primary_icon_tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="placeholder_text" translatable="yes">Port</property>
<signal name="changed" handler="on_port_changed" swapped="no"/>
<signal name="insert-text" handler="on_port_insert_text" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="port_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Port:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="sandbox_label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">SANDBOX</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="pid_title">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Pid:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="pid_label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">PID_LABEL</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="scope_label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">SCOPE_LABEL</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">9</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="homogeneous">True</property>
<property name="baseline_position">top</property>
<child>
<object class="GtkComboBoxText" id="uid_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<property name="active_id">-1</property>
<items>
<item id="-1">Any User</item>
</items>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="uid_checkbox">
<property name="label" translatable="yes">Apply</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkComboBoxText" id="gid_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<property name="active_id">-1</property>
<items>
<item id="-1">Any Group</item>
</items>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="gid_checkbox">
<property name="label" translatable="yes">Apply</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">8</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="path_entry">
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="origin_label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="tooltip_markup" translatable="yes">The character &lt;b&gt;*&lt;/b&gt; can be use to match any value.</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Origin:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="origin_entry">
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="scope_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<property name="active_id">0</property>
<items>
<item id="2" translatable="yes">Permanent</item>
<item id="0" translatable="yes">Session</item>
<item id="1" translatable="yes">Process</item>
<item id="4" translatable="yes">Once</item>
<item id="3" translatable="yes">System</item>
</items>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">9</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkComboBoxText" id="proto_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">1</property>
<property name="active_id">1</property>
<items>
<item translatable="yes">Any</item>
<item id="tcp" translatable="yes">TCP</item>
<item id="udp" translatable="yes">UDP</item>
<item id="icmp" translatable="yes">ICMP</item>
</items>
<signal name="changed" handler="on_proto_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="tls_check">
<property name="label" translatable="yes">TLS Only</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">6</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="3">allow_button</action-widget>
<action-widget response="1">cancel_button</action-widget>
<action-widget response="2">ok_button</action-widget>
</action-widgets>
</object>
</interface>
`
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,38 @@
package main
import (
"fmt"
"os"
"github.com/subgraph/oz"
)
var ozProfiles oz.Profiles
func init() {
c, err := oz.LoadConfig(oz.DefaultConfigPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to read oz config...")
os.Exit(1)
}
p, err := oz.LoadProfiles(c.ProfileDir)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to read oz profiles...")
os.Exit(1)
}
ozProfiles = p
}
func (fa *fwApp) initOZProfiles() {
for _, p := range ozProfiles {
// XXX: This actually should match against sgfw's opened sockets
switch {
case string(p.Networking.Nettype) == "host":
fallthrough
case len(p.Networking.Sockets) == 0:
continue
default:
fa.ozProfiles = append(fa.ozProfiles, p.Name)
}
}
}

@ -0,0 +1,901 @@
package main
import (
"errors"
"fmt"
"log"
spath "path"
"strings"
"strconv"
"sync"
"time"
"github.com/subgraph/fw-daemon/sgfw"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
)
type Prompt struct {
app *fwApp
tv *gtk.TreeView
ts *gtk.TreeStore
stack *gtk.Stack
pecols []*gtk.TreeViewColumn
pncol *gtk.TreeViewColumn
promptLock *sync.Mutex
recentLock *sync.Mutex
config *sgfw.FirewallConfigs
recentlyRemoved []string
}
const (
COL_NO_NREFS = iota
COL_NO_ICON_PIXBUF
COL_NO_GUID
COL_NO_PATH
COL_NO_SANDBOX
COL_NO_ICON
COL_NO_PROTO
COL_NO_PID
COL_NO_DSTIP
COL_NO_HOSTNAME
COL_NO_PORT
COL_NO_UID
COL_NO_GID
COL_NO_ORIGIN
COL_NO_TIMESTAMP
COL_NO_IS_SOCKS
COL_NO_OPTSTRING
COL_NO_ACTION
COL_NO_FILLER
COL_NO_LAST
)
type ruleColumns struct {
nrefs int
Path string
Sandbox string
GUID string
Icon string
Proto string
Pid int
Target string
Hostname string
Port int
UID int
GID int
Uname string
Gname string
Origin string
Timestamp string
IsSocks bool
ForceTLS bool
Scope int
}
func createPromptView(app *fwApp, sw *gtk.ScrolledWindow) (*Prompt, error) {
var err error
p := &Prompt{}
p.app = app
p.promptLock = &sync.Mutex{}
p.recentLock = &sync.Mutex{}
p.tv, err = gtk.TreeViewNew()
if err != nil {
return nil, err
}
p.tv.SetSizeRequest(300, 300)
p.tv.SetHeadersClickable(true)
p.tv.SetEnableSearch(false)
p.tv.AppendColumn(createColumnText("#", COL_NO_NREFS))
p.tv.AppendColumn(createColumnImg("", COL_NO_ICON_PIXBUF))
guidcol := createColumnText("GUID", COL_NO_GUID)
guidcol.SetVisible(false)
p.tv.AppendColumn(guidcol)
p.tv.AppendColumn(createColumnText("Path", COL_NO_PATH))
sbcol := createColumnText("Sandbox", COL_NO_SANDBOX)
sbcol.SetVisible(false)
p.tv.AppendColumn(sbcol)
icol := createColumnText("Icon", COL_NO_ICON)
icol.SetVisible(false)
p.tv.AppendColumn(icol)
var pecol *gtk.TreeViewColumn
p.tv.AppendColumn(createColumnText("Protocol", COL_NO_PROTO))
pecol = createColumnText("PID", COL_NO_PID)
p.tv.AppendColumn(pecol)
p.pecols = append(p.pecols, pecol)
p.tv.AppendColumn(createColumnText("IP Address", COL_NO_DSTIP))
pecol = createColumnText("Hostname", COL_NO_HOSTNAME)
pecol.SetMinWidth(64)
p.tv.AppendColumn(pecol)
p.tv.AppendColumn(createColumnText("Port", COL_NO_PORT))
pecol = createColumnText("UID", COL_NO_UID)
p.tv.AppendColumn(pecol)
p.pecols = append(p.pecols, pecol)
pecol = createColumnText("GID", COL_NO_GID)
p.tv.AppendColumn(pecol)
p.pecols = append(p.pecols, pecol)
pecol = createColumnText("Origin", COL_NO_ORIGIN)
p.tv.AppendColumn(pecol)
p.pecols = append(p.pecols, pecol)
pecol = createColumnText("Timestamp", COL_NO_TIMESTAMP)
p.tv.AppendColumn(pecol)
p.pecols = append(p.pecols, pecol)
scol := createColumnText("Is SOCKS", COL_NO_IS_SOCKS)
scol.SetVisible(false)
p.tv.AppendColumn(scol)
pecol = createColumnText("Details", COL_NO_OPTSTRING)
p.tv.AppendColumn(pecol)
p.pecols = append(p.pecols, pecol)
acol := createColumnText("Scope", COL_NO_ACTION)
acol.SetVisible(false)
p.tv.AppendColumn(acol)
pncol := createColumnImg("", COL_NO_FILLER)
pncol.SetVisible(false)
pncol.SetSortIndicator(false)
p.tv.AppendColumn(pncol)
p.pncol = pncol
p.togglePECols()
p.ts = createTreeStore(true)
p.tv.SetModel(p.ts)
p.tv.Connect("row-activated", func() {
p.promptLock.Lock()
seldata, _, _, err := p.getSelectedRule()
p.promptLock.Unlock()
if err != nil {
warnDialog(&p.app.win.Window, "Unexpected error reading selected rule: " + err.Error() + "\n" + fmt.Sprintf("%+v", seldata))
return
}
rl := &ruleList{app: p.app}
target := seldata.Hostname
if target == "" {
target = seldata.Target
}
rr := &ruleRow{ rl: rl, rule: &sgfw.DbusRule{
Path: seldata.Path,
Sandbox: seldata.Sandbox,
Pid: uint32(seldata.Pid),
UID: int32(seldata.UID),
GID: int32(seldata.GID),
Target: strings.Join([]string{target, strconv.Itoa(seldata.Port)}, ":"),
Proto: seldata.Proto,
Origin: seldata.Origin,
IsSocks: seldata.IsSocks,
}}
redit := newRuleAdd(rr, DIALOG_MODE_PROMPT)
redit.update()
redit.run(seldata.GUID, p.buttonAction)
return
})
p.app.appendConfigCallback(p.togglePECols)
sw.SetSizeRequest(600, 400)
p.createShortcuts()
sw.Add(p.tv)
return p, nil
}
func (p *Prompt) HasItems() bool {
return p.ts.IterNChildren(nil) > 0
}
func (p *Prompt) togglePECols() {
v := p.app.Config.PromptExpanded
for _, pc := range p.pecols {
pc.SetVisible(v)
}
p.pncol.SetVisible(!v)
}
func (p *Prompt) createShortcuts() {
// We register here since the shortcuts are bound in an ephemeral window
p.app.RegisterShortcutHelp("<Alt>a", "prompt", "Allow")
p.app.RegisterShortcutHelp("<Alt>d Escape", "prompt", "Deny")
p.app.RegisterShortcutHelp("<Alt>c", "prompt", "Cancel")
p.app.RegisterShortcutHelp("<Alt>h", "prompt", "Select the hostname/IP entry")
p.app.RegisterShortcutHelp("<Alt>p", "prompt", "Select the port entry")
p.app.RegisterShortcutHelp("<Alt>o", "prompt", "Select protocol")
p.app.RegisterShortcutHelp("<Alt>t", "prompt", "Toggle allow TLS only")
p.app.RegisterShortcutHelp("<Alt>s", "prompt", "Select scope")
p.app.RegisterShortcutHelp("<Alt>u", "prompt", "Toggle apply UID")
p.app.RegisterShortcutHelp("<Alt>g", "prompt", "Toggle apply GID")
p.app.ConnectShortcut("<Primary><Alt>space", "", "", p.app.win.Window, func (win gtk.Window) {
vis := p.app.tlStack.GetVisibleChildName()
iter, found := p.ts.GetIterFirst()
if iter == nil || found == false && vis != "prompt" {
return
}
if vis != "prompt" {
p.app.tlStack.SetVisibleChildFull("prompt", gtk.STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT)
p.app.onStackChanged()
}
pi, _ := p.ts.GetPath(iter)
if pi != nil {
p.tv.SetCursor(pi, nil, false)
p.tv.Emit("row-activated")
}
})
}
func createColumnImg(title string, id int) *gtk.TreeViewColumn {
cellRenderer, err := gtk.CellRendererPixbufNew()
if err != nil {
log.Fatal("Unable to create image cell renderer:", err)
}
column, err := gtk.TreeViewColumnNewWithAttribute(title, cellRenderer, "pixbuf", id)
if err != nil {
log.Fatal("Unable to create cell column:", err)
}
return column
}
func createColumnText(title string, id int) *gtk.TreeViewColumn {
cellRenderer, err := gtk.CellRendererTextNew()
if err != nil {
log.Fatal("Unable to create text cell renderer:", err)
}
column, err := gtk.TreeViewColumnNewWithAttribute(title, cellRenderer, "text", id)
if err != nil {
log.Fatal("Unable to create cell column:", err)
}
column.SetSortColumnID(id)
column.SetResizable(true)
return column
}
func createTreeStore(general bool) *gtk.TreeStore {
colData := []glib.Type{glib.TYPE_INT, glib.TYPE_OBJECT, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_STRING,
glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_INT, glib.TYPE_INT, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_OBJECT}
treeStore, err := gtk.TreeStoreNew(colData...)
if err != nil {
log.Fatal("Unable to create list store:", err)
}
return treeStore
}
func (p *Prompt) addRequestInc(guid, path, icon, proto string, pid int, ipaddr, hostname string, port, uid, gid int,
origin, timestamp string, is_socks bool, optstring string, sandbox string, action int) bool {
duplicated := false
p.promptLock.Lock()
defer p.promptLock.Unlock()
for ridx := 0; ridx < p.ts.IterNChildren(nil); ridx++ {
rule, iter, err := p.getRuleByIdx(ridx, -1)
if err != nil {
break
// XXX: not compared: optstring/sandbox
} else if (rule.Path == path) && (rule.Proto == proto) && (rule.Pid == pid) && (rule.Target == ipaddr) && (rule.Hostname == hostname) &&
(rule.Port == port) && (rule.UID == uid) && (rule.GID == gid) && (rule.Origin == origin) && (rule.IsSocks == is_socks) {
rule.nrefs++
err := p.ts.SetValue(iter, 0, rule.nrefs)
if err != nil {
fmt.Println("Error creating duplicate firewall prompt entry:", err)
break
}
duplicated = true
subiter := p.ts.Append(iter)
p.storeNewEntry(subiter, guid, path, sandbox, icon, proto, pid, ipaddr, hostname, port, uid, gid, origin, timestamp, is_socks, optstring, action)
break
}
}
return duplicated
}
func (p *Prompt) AddRequest(guid, path, icon, proto string, pid int, ipaddr, hostname string, port, uid, gid int,
origin, timestamp string, is_socks bool, optstring string, sandbox string, action int) bool {
if p.ts == nil {
waitTimes := []int{1, 2, 5, 10}
if p.ts == nil {
fmt.Println("SGFW prompter was not ready to receive firewall request... waiting")
for _, wtime := range waitTimes {
time.Sleep(time.Duration(wtime) * time.Second)
if p.ts != nil {
break
}
fmt.Println("SGFW prompter is still waiting...")
}
}
}
if p.ts == nil {
log.Fatal("SGFW prompter GUI failed to load for unknown reasons")
}
if p.addRequestInc(guid, path, icon, proto, pid, ipaddr, hostname, port, uid, gid, origin, timestamp, is_socks, optstring, sandbox, action) {
fmt.Println("Request was duplicate: ", guid)
p.promptLock.Lock()
p.toggleHover()
p.promptLock.Unlock()
return true
}
p.promptLock.Lock()
defer p.promptLock.Unlock()
iter := p.ts.Append(nil)
p.storeNewEntry(iter, guid, path, sandbox, icon, proto, pid, ipaddr, hostname, port, uid, gid, origin, timestamp, is_socks, optstring, action)
p.toggleHover()
return true
}
// Needs to be locked by caller
func (p *Prompt)storeNewEntry(iter *gtk.TreeIter, guid, path, sandbox, icon, proto string, pid int, ipaddr, hostname string, port, uid, gid int, origin,
timestamp string, is_socks bool, optstring string, action int) {
var colVals = [COL_NO_LAST]interface{}{}
if is_socks {
if (optstring != "") && (strings.Index(optstring, "SOCKS") == -1) {
optstring = "SOCKS5 / " + optstring
} else if optstring == "" {
optstring = "SOCKS5"
}
}
colVals[COL_NO_NREFS] = 1
colVals[COL_NO_ICON_PIXBUF] = nil
colVals[COL_NO_GUID] = guid
colVals[COL_NO_PATH] = path
colVals[COL_NO_SANDBOX] = sandbox
colVals[COL_NO_ICON] = icon
colVals[COL_NO_PROTO] = proto
colVals[COL_NO_PID] = pid
if ipaddr == "" {
colVals[COL_NO_DSTIP] = "---"
} else {
colVals[COL_NO_DSTIP] = ipaddr
}
colVals[COL_NO_HOSTNAME] = hostname
colVals[COL_NO_PORT] = port
colVals[COL_NO_UID] = uid
colVals[COL_NO_GID] = gid
colVals[COL_NO_ORIGIN] = origin
colVals[COL_NO_TIMESTAMP] = timestamp
colVals[COL_NO_IS_SOCKS] = 0
if is_socks {
colVals[COL_NO_IS_SOCKS] = 1
}
colVals[COL_NO_OPTSTRING] = optstring
colVals[COL_NO_ACTION] = action
colVals[COL_NO_FILLER] = nil
itheme, err := gtk.IconThemeGetDefault()
if err != nil {
log.Fatal("Could not load default icon theme:", err)
}
in := []string{spath.Base(path)}
if sandbox != "" {
in = append([]string{sandbox}, in...)
}
if icon != "" {
in = append(in, icon)
}
in = append(in, "terminal")
if path == "[unknown]" {
in = []string{"image-missing"}
}
for _, ia := range in {
pb, _ := itheme.LoadIcon(ia, int(gtk.ICON_SIZE_BUTTON), gtk.ICON_LOOKUP_GENERIC_FALLBACK)
if pb != nil {
colVals[COL_NO_ICON_PIXBUF] = pb
break
}
}
pb, err := gdk.PixbufNew(gdk.COLORSPACE_RGB, true, 8, 24, 24)
if err != nil {
log.Println("Error creating blank icon:", err)
} else {
colVals[COL_NO_FILLER] = pb
img, err := gtk.ImageNewFromPixbuf(pb)
if err != nil {
log.Println("Error creating image from pixbuf:", err)
} else {
img.Clear()
pb = img.GetPixbuf()
colVals[COL_NO_FILLER] = pb
}
}
for n := 0; n < len(colVals); n++ {
err := p.ts.SetValue(iter, n, colVals[n])
if err != nil {
log.Fatal("Unable to add row:", err)
}
}
return
}
func (p *Prompt) getRuleByIdx(idx, subidx int) (ruleColumns, *gtk.TreeIter, error) {
rule := ruleColumns{}
tpath := fmt.Sprintf("%d", idx)
if subidx != -1 {
tpath = fmt.Sprintf("%d:%d", idx, subidx)
}
path, err := gtk.TreePathNewFromString(tpath)
if err != nil {
return rule, nil, err
}
iter, err := p.ts.GetIter(path)
if err != nil {
return rule, nil, err
}
rule.nrefs, err = p.lsGetInt(iter, COL_NO_NREFS)
if err != nil {
return rule, nil, err
}
rule.GUID, err = p.lsGetStr(iter, COL_NO_GUID)
if err != nil {
return rule, nil, err
}
rule.Path, err = p.lsGetStr(iter, COL_NO_PATH)
if err != nil {
return rule, nil, err
}
rule.Sandbox, err = p.lsGetStr(iter, COL_NO_SANDBOX)
if err != nil {
return rule, nil, err
}
rule.Icon, err = p.lsGetStr(iter, COL_NO_ICON)
if err != nil {
return rule, nil, err
}
rule.Proto, err = p.lsGetStr(iter, COL_NO_PROTO)
if err != nil {
return rule, nil, err
}
rule.Pid, err = p.lsGetInt(iter, COL_NO_PID)
if err != nil {
return rule, nil, err
}
rule.Target, err = p.lsGetStr(iter, COL_NO_DSTIP)
if err != nil {
return rule, nil, err
}
rule.Hostname, err = p.lsGetStr(iter, COL_NO_HOSTNAME)
if err != nil {
return rule, nil, err
}
rule.Port, err = p.lsGetInt(iter, COL_NO_PORT)
if err != nil {
return rule, nil, err
}
rule.UID, err = p.lsGetInt(iter, COL_NO_UID)
if err != nil {
return rule, nil, err
}
rule.GID, err = p.lsGetInt(iter, COL_NO_GID)
if err != nil {
return rule, nil, err
}
rule.Origin, err = p.lsGetStr(iter, COL_NO_ORIGIN)
if err != nil {
return rule, nil, err
}
rule.Timestamp, err = p.lsGetStr(iter, COL_NO_TIMESTAMP)
if err != nil {
return rule, nil, err
}
rule.IsSocks = false
is_socks, err := p.lsGetInt(iter, COL_NO_IS_SOCKS)
if err != nil {
return rule, nil, err
}
if is_socks != 0 {
rule.IsSocks = true
}
rule.Scope, err = p.lsGetInt(iter, COL_NO_ACTION)
if err != nil {
return rule, nil, err
}
return rule, iter, nil
}
func (p *Prompt) lsGetInt(iter *gtk.TreeIter, idx int) (int, error) {
val, err := p.ts.GetValue(iter, idx)
if err != nil {
return 0, err
}
ival, err := val.GoValue()
if err != nil {
return 0, err
}
return ival.(int), nil
}
func (p *Prompt) lsGetStr(iter *gtk.TreeIter, idx int) (string, error) {
val, err := p.ts.GetValue(iter, idx)
if err != nil {
return "", err
}
sval, err := val.GetString()
if err != nil {
return "", err
}
return sval, nil
}
func (p *Prompt) toggleHover() {
nitems := p.ts.IterNChildren(nil)
stack := p.app.tlStack.GetChildByName("prompt")
if nitems > 0 {
if p.app.Settings.GetToplevelPrompt() {
//p.win.SetModal(true)
p.app.win.Deiconify()
p.app.win.SetKeepAbove(true)
p.app.win.Stick()
}
p.app.win.SetUrgencyHint(true)
p.app.win.Present()
if p.app.tlStack.GetVisibleChildName() != "prompt" {
err := p.app.tlStack.ChildSetProperty(stack, "needs-attention", true)
if err != nil {
fmt.Println("Error setting stack attention")
}
}
} else {
//p.win.SetModal(false)
p.app.win.SetUrgencyHint(false)
if p.app.Settings.GetToplevelPrompt() {
p.app.win.SetKeepAbove(false)
p.app.win.Unstick()
}
p.app.tlStack.ChildSetProperty(stack, "needs-attention", false)
}
}
// Needs to be locked by the caller
func (p *Prompt) getSelectedRule() (ruleColumns, int, int, error) {
rule := ruleColumns{}
sel, err := p.tv.GetSelection()
if err != nil {
return rule, -1, -1, err
}
rows := sel.GetSelectedRows(p.ts)
if rows.Length() <= 0 {
return rule, -1, -1, errors.New("no selection was made")
}
rdata := rows.NthData(0)
tpath := rdata.(*gtk.TreePath).String()
subidx := -1
ptoks := strings.Split(tpath, ":")
if len(ptoks) > 2 {
return rule, -1, -1, errors.New("internal error parsing selected item tree path")
} else if len(ptoks) == 2 {
subidx, err = strconv.Atoi(ptoks[1])
if err != nil {
return rule, -1, -1, err
}
tpath = ptoks[0]
}
lIndex, err := strconv.Atoi(tpath)
if err != nil {
return rule, -1, -1, err
}
// fmt.Printf("lindex = %d : %d\n", lIndex, subidx)
rule, _, err = p.getRuleByIdx(lIndex, subidx)
if err != nil {
return rule, -1, -1, err
}
return rule, lIndex, subidx, nil
}
// Needs to be locked by the caller
func (p *Prompt) numSelections() int {
sel, err := p.tv.GetSelection()
if err != nil {
return -1
}
rows := sel.GetSelectedRows(p.ts)
return int(rows.Length())
}
func (p *Prompt) removeSelectedRule(idx, subidx int) error {
fmt.Printf("XXX: attempting to remove idx = %v, %v\n", idx, subidx)
ppathstr := fmt.Sprintf("%d", idx)
pathstr := ppathstr
if subidx > -1 {
pathstr = fmt.Sprintf("%d:%d", idx, subidx)
}
iter, err := p.ts.GetIterFromString(pathstr)
if err != nil {
return err
}
nchildren := p.ts.IterNChildren(iter)
if nchildren >= 1 {
firstpath := fmt.Sprintf("%d:0", idx)
citer, err := p.ts.GetIterFromString(firstpath)
if err != nil {
return err
}
gnrefs, err := p.ts.GetValue(iter, COL_NO_NREFS)
if err != nil {
return err
}
vnrefs, err := gnrefs.GoValue()
if err != nil {
return err
}
nrefs := vnrefs.(int) - 1
for n := 0; n < COL_NO_LAST; n++ {
val, err := p.ts.GetValue(citer, n)
if err != nil {
return err
}
if n == COL_NO_NREFS {
err = p.ts.SetValue(iter, n, nrefs)
} else {
err = p.ts.SetValue(iter, n, val)
}
if err != nil {
return err
}
}
p.ts.Remove(citer)
return nil
}
p.ts.Remove(iter)
if subidx > -1 {
ppath, err := gtk.TreePathNewFromString(ppathstr)
if err != nil {
return err
}
piter, err := p.ts.GetIter(ppath)
if err != nil {
return err
}
nrefs, err := p.lsGetInt(piter, COL_NO_NREFS)
if err != nil {
return err
}
err = p.ts.SetValue(piter, COL_NO_NREFS, nrefs-1)
if err != nil {
return err
}
}
p.toggleHover()
return nil
}
func (p *Prompt) addRecentlyRemoved(guid string) {
p.recentLock.Lock()
defer p.recentLock.Unlock()
fmt.Println("RECENTLY REMOVED: ", guid)
p.recentlyRemoved = append(p.recentlyRemoved, guid)
}
func (p *Prompt) wasRecentlyRemoved(guid string) bool {
p.recentLock.Lock()
defer p.recentLock.Unlock()
for gind, g := range p.recentlyRemoved {
if g == guid {
p.recentlyRemoved = append(p.recentlyRemoved[:gind], p.recentlyRemoved[gind+1:]...)
return true
}
}
return false
}
func (p *Prompt) RemoveRequest(guid string) {
if p.wasRecentlyRemoved(guid) {
fmt.Printf("Entry for %s was recently removed; deleting from cache\n", guid)
return
}
removed := false
if p.ts == nil {
return
}
p.promptLock.Lock()
defer p.promptLock.Unlock()
remove_outer:
for ridx := 0; ridx < p.ts.IterNChildren(nil); ridx++ {
nchildren := 0
this_iter, err := p.ts.GetIterFromString(fmt.Sprintf("%d", ridx))
if err != nil {
log.Println("Strange condition; couldn't get iter of known tree index:", err)
} else {
nchildren = p.ts.IterNChildren(this_iter)
}
for cidx := 0; cidx < nchildren-1; cidx++ {
sidx := cidx
if cidx == nchildren {
cidx = -1
}
rule, _, err := p.getRuleByIdx(ridx, sidx)
if err != nil {
break remove_outer
} else if rule.GUID == guid {
p.removeSelectedRule(ridx, sidx)
removed = true
break
}
}
}
if !removed {
fmt.Printf("Unexpected condition: SGFW requested prompt removal for non-existent GUID %v\n", guid)
}
}
func (p *Prompt) RemoveAll() {
p.promptLock.Lock()
defer p.promptLock.Unlock()
p.recentLock.Lock()
defer p.recentLock.Unlock()
p.recentlyRemoved = p.recentlyRemoved[:0]
for {
iter, found := p.ts.GetIterFirst()
if iter == nil || found == false {
break
}
pi, _ := p.ts.GetPath(iter)
if pi == nil {
break
}
p.tv.SetCursor(pi, nil, false)
_, idx, subidx, err := p.getSelectedRule()
if err != nil {
break
}
p.removeSelectedRule(idx, subidx)
}
}
func (p *Prompt) makeDecision(rule string, scope int, guid string) error {
return p.app.Dbus.answerPrompt(uint32(scope), rule, guid)
}
func (p *Prompt) buttonAction(guid string, rr *sgfw.DbusRule) {
p.promptLock.Lock()
rule, idx, subidx, err := p.getSelectedRule()
if err != nil {
p.promptLock.Unlock()
warnDialog(&p.app.win.Window, "Error occurred processing request: %s", err.Error())
return
}
tk := strings.Split(rr.Target, ":")
// Overlay the rules
rule.Scope = int(rr.Mode)
//rule.Path = urule.Path
rule.Port, _ = strconv.Atoi(tk[1])
rule.Target = tk[0]
rule.Proto = rr.Proto
rule.UID = int(rr.UID)
rule.GID = int(rr.GID)
// rule.Uname = urule.Uname
// rule.Gname = urule.Gname
fmt.Println("rule = ", rule)
action := sgfw.RuleActionString[sgfw.RuleAction(rr.Verb)]
rulestr := action
proto := rule.Proto
if proto == "any" || proto == "" {
proto = "*"
}
rulestr += "|" + proto + ":" + rule.Target + ":" + strconv.Itoa(rule.Port)
rulestr += "|" + sgfw.RuleModeString[sgfw.RuleMode(rule.Scope)]
rulestr += "|" + strconv.Itoa(rule.UID) + ":" + strconv.Itoa(rule.GID)
if rule.Sandbox != "" {
rulestr += "|" + rule.Sandbox
}
fmt.Println("RULESTR = ", rulestr)
p.makeDecision(rulestr, int(rule.Scope), guid)
err = p.removeSelectedRule(idx, subidx)
p.addRecentlyRemoved(guid)
p.promptLock.Unlock()
if err != nil {
warnDialog(&p.app.win.Window, "Error setting new rule: %s", err.Error())
}
}

@ -87,7 +87,7 @@ func (re *ruleEdit) validateFields() bool {
if !isValidHost(host) { if !isValidHost(host) {
return false return false
} }
if !isValidPort(port) { if !isValidPort(port, "") {
return false return false
} }
return true return true
@ -113,7 +113,8 @@ func isValidHost(host string) bool {
return true return true
} }
func isValidPort(port string) bool { func isValidPort(port, proto string) bool {
min := 0
if port == "*" { if port == "*" {
return true return true
} }
@ -122,7 +123,11 @@ func isValidPort(port string) bool {
if err != nil { if err != nil {
return false return false
} }
return pval > 0 && pval <= 0xFFFF
if proto == "icmp" {
min = -1
}
return pval > min && pval <= 0xFFFF
} }
func (re *ruleEdit) updateRow() { func (re *ruleEdit) updateRow() {
@ -145,15 +150,15 @@ func (re *ruleEdit) updateRow() {
} }
func (re *ruleEdit) run(saveasnew bool) { func (re *ruleEdit) run(saveasnew bool) {
re.dialog.SetTransientFor(re.row.rl.win) re.dialog.SetTransientFor(&re.row.rl.app.win.Window)
if re.dialog.Run() == editDialogOk { if re.dialog.Run() == editDialogOk {
if saveasnew { if saveasnew {
re.row.rule.Mode = uint16(sgfw.RULE_MODE_PERMANENT) re.row.rule.Mode = uint16(sgfw.RULE_MODE_PERMANENT)
} }
re.updateRow() re.updateRow()
re.row.rl.dbus.updateRule(re.row.rule) re.row.rl.app.Dbus.updateRule(re.row.rule)
if saveasnew { if saveasnew {
re.row.widget.Hide() re.row.Hide()
} }
} }
re.dialog.Destroy() re.dialog.Destroy()

@ -0,0 +1,551 @@
package main
import (
"fmt"
"os"
"strings"
"strconv"
"unicode"
"github.com/subgraph/fw-daemon/sgfw"
"github.com/gotk3/gotk3/gtk"
)
const (
newDialogCancel = 1
newDialogOk = 2
newDialogAllow = 3
)
const (
COLUMN_ID = iota
COLUMN_NAME
)
type DialogMode uint
const (
DIALOG_MODE_NEW DialogMode = iota
DIALOG_MODE_EDIT
DIALOG_MODE_SAVEAS
DIALOG_MODE_PROMPT
DIALOG_MODE_INFO
)
const (
Setuid uint32 = 1 << (12 - 1 - iota)
Setgid
Sticky
UserRead
UserWrite
UserExecute
GroupRead
GroupWrite
GroupExecute
OtherRead
OtherWrite
OtherExecute
)
type ruleNew struct {
dialog *gtk.Dialog
row *ruleRow
mode DialogMode
nbSelected int
comboUID *gtk.ComboBoxText
checkUID *gtk.CheckButton
comboGID *gtk.ComboBoxText
checkGID *gtk.CheckButton
titleScope *gtk.Label
comboScope *gtk.ComboBoxText
labelScope *gtk.Label
comboVerb *gtk.ComboBoxText
checkTLS *gtk.CheckButton
titleSandbox *gtk.Label
labelSandbox *gtk.Label
comboSandbox *gtk.ComboBoxText
btnPathChooser *gtk.FileChooserButton
entryPath *gtk.Entry
hostEntry *gtk.Entry
portEntry *gtk.Entry
titlePort *gtk.Label
comboProto *gtk.ComboBoxText
ok *gtk.Button
allow *gtk.Button
cancel *gtk.Button
labelPID *gtk.Label
titlePID *gtk.Label
entryOrigin *gtk.Entry
labelOrigin *gtk.Label
}
func newRuleAdd(rr *ruleRow, mode DialogMode) *ruleNew{
rnew := &ruleNew{}
rnew.mode = mode
rnew.nbSelected = rr.rl.app.nbRules.GetCurrentPage()
b := newBuilder("RuleNew")
b.getItems(
"dialog", &rnew.dialog,
"uid_combo", &rnew.comboUID,
"uid_checkbox", &rnew.checkUID,
"gid_combo", &rnew.comboGID,
"gid_checkbox", &rnew.checkGID,
"scope_title", &rnew.titleScope,
"scope_combo", &rnew.comboScope,
"scope_label", &rnew.labelScope,
"verb_combo", &rnew.comboVerb,
"tls_check", &rnew.checkTLS,
"sandbox_title", &rnew.titleSandbox,
"sandbox_combo", &rnew.comboSandbox,
"sandbox_label", &rnew.labelSandbox,
"path_chooser", &rnew.btnPathChooser,
"path_entry", &rnew.entryPath,
"host_entry", &rnew.hostEntry,
"port_entry", &rnew.portEntry,
"port_title", &rnew.titlePort,
"proto_combo", &rnew.comboProto,
"ok_button", &rnew.ok,
"allow_button", &rnew.allow,
"cancel_button", &rnew.cancel,
"pid_label", &rnew.labelPID,
"pid_title", &rnew.titlePID,
"origin_entry", &rnew.entryOrigin,
"origin_label", &rnew.labelOrigin,
)
b.ConnectSignals(map[string]interface{}{
"on_proto_changed": rnew.onProtoChanged,
"on_verb_changed": rnew.onVerbChanged,
"on_port_insert_text": rnew.onPortInsertText,
"on_port_changed": rnew.onChanged,
"on_host_changed": rnew.onChanged,
"on_path_changed": rnew.onChanged,
"on_path_set": rnew.onPathSet,
})
rnew.row = rr
switch rnew.mode {
case DIALOG_MODE_EDIT:
rnew.dialog.SetTitle("Edit Rule")
case DIALOG_MODE_NEW:
rnew.dialog.SetTitle("Add New Rule")
case DIALOG_MODE_SAVEAS:
rnew.ok.SetLabel("Save As New")
rnew.dialog.SetTitle("Save As New Rule")
case DIALOG_MODE_PROMPT:
rnew.connectShortcutsPromptWindow()
rnew.dialog.SetTitle("Firewall Prompt")
case DIALOG_MODE_INFO:
rnew.cancel.SetLabel("Close")
rnew.dialog.SetTitle("Rule Information")
}
return rnew
}
func (re *ruleNew) connectShortcutsPromptWindow() {
app := re.row.rl.app
// Shortcuts Help Registered in Prompt
app.ConnectShortcut("<Alt>h", "", "", re.dialog.Window, func(win gtk.Window) {re.hostEntry.Widget.GrabFocus()})
app.ConnectShortcut("<Alt>p", "", "", re.dialog.Window, func(win gtk.Window) {re.portEntry.Widget.GrabFocus()})
app.ConnectShortcut("<Alt>o", "", "", re.dialog.Window, func(win gtk.Window) {re.comboProto.ComboBox.Popup()})
app.ConnectShortcut("<Alt>t", "", "", re.dialog.Window, func(win gtk.Window) {
if re.checkTLS.GetSensitive() {
re.checkTLS.SetActive(!re.checkTLS.GetActive())
}
})
app.ConnectShortcut("<Alt>s", "", "", re.dialog.Window, func(win gtk.Window) {re.comboScope.ComboBox.Popup()})
app.ConnectShortcut("<Alt>u", "", "", re.dialog.Window, func(win gtk.Window) {re.checkUID.SetActive(!re.checkUID.GetActive())})
app.ConnectShortcut("<Alt>g", "", "", re.dialog.Window, func(win gtk.Window) {re.checkGID.SetActive(!re.checkGID.GetActive())})
}
func (re *ruleNew) updateRow(res int) {
if !re.validateFields() {
return
}
r := re.row.rule
if re.mode == DIALOG_MODE_PROMPT {
if res == newDialogOk {
r.Verb = uint16(sgfw.RULE_ACTION_DENY)
} else if res == newDialogAllow {
r.Verb = uint16(sgfw.RULE_ACTION_ALLOW)
}
mid, _ := strconv.Atoi(re.comboScope.GetActiveID())
r.Mode = uint16(mid)
} else {
switch re.comboVerb.GetActiveID() {
case "allow":
r.Verb = uint16(sgfw.RULE_ACTION_ALLOW)
// case "allow_tls":
// r.Verb = uint16(sgfw.RULE_ACTION_ALLOW_TLSONLY)
case "deny":
r.Verb = uint16(sgfw.RULE_ACTION_DENY)
}
}
r.Proto = re.comboProto.GetActiveID()
if r.Proto == "any" {
r.Proto = "*"
}
if r.Proto == "tcp" && r.Verb == uint16(sgfw.RULE_ACTION_ALLOW) && re.checkTLS.GetActive() {
r.Verb = uint16(sgfw.RULE_ACTION_ALLOW_TLSONLY)
}
host, _ := re.hostEntry.GetText()
port, _ := re.portEntry.GetText()
r.Target = fmt.Sprintf("%s:%s", host, port)
if re.mode != DIALOG_MODE_PROMPT || re.checkUID.GetActive() == true {
uid, _ := strconv.ParseInt(re.comboUID.GetActiveID(), 10, 32)
r.UID = int32(uid)
} else {
r.UID = -1
}
if re.mode != DIALOG_MODE_PROMPT || re.checkGID.GetActive() == true {
gid, _ := strconv.ParseInt(re.comboGID.GetActiveID(), 10, 32)
r.GID = int32(gid)
} else {
r.GID = -1
}
if re.mode == DIALOG_MODE_NEW {
r.Path = re.btnPathChooser.FileChooser.GetFilename()
mid, _ := strconv.Atoi(re.comboScope.GetActiveID())
r.Mode = uint16(mid)
r.Sandbox = re.comboSandbox.GetActiveID()
}
if re.mode != DIALOG_MODE_NEW && re.mode != DIALOG_MODE_PROMPT {
re.row.update()
}
}
type cbPromptRequest func(guid string, rule *sgfw.DbusRule)
func (re *ruleNew) run(guid string, cb cbPromptRequest) {
re.dialog.SetTransientFor(re.row.rl.app.win)
re.dialog.ShowAll()
if re.mode == DIALOG_MODE_INFO {
re.dialog.Run()
} else if re.mode == DIALOG_MODE_PROMPT {
res := re.dialog.Run()
if res != newDialogCancel {
re.updateRow(res)
cb(guid, re.row.rule)
}
} else if re.mode == DIALOG_MODE_NEW {
if re.dialog.Run() == newDialogOk {
re.updateRow(newDialogOk)
r := *re.row.rule
res, err := re.row.rl.app.Dbus.addRule(&r)
if res == false || err != nil {
warnDialog(&re.row.rl.app.win.Window, "Error notifying SGFW of asynchronous rule addition:", err)
return
}
}
} else if re.mode == DIALOG_MODE_SAVEAS {
if re.dialog.Run() == newDialogOk {
re.updateRow(newDialogOk)
r := *re.row.rule
re.row.rl.app.Dbus.addRule(&r)
re.row.rl.remove(re.row)
}
} else {
if re.dialog.Run() == newDialogOk {
re.updateRow(newDialogOk)
re.row.rl.app.Dbus.updateRule(re.row.rule)
}
}
re.dialog.Destroy()
}
func (rr *ruleRow) runNewEditor(mode DialogMode) {
redit := newRuleAdd(rr, mode)
redit.update()
redit.run("", nil)
}
func (re *ruleNew) update() {
re.populateUID()
re.populateGID()
r := re.row.rule
if re.mode != DIALOG_MODE_INFO {
re.comboScope.Remove(4)
}
if re.mode != DIALOG_MODE_PROMPT && re.mode != DIALOG_MODE_INFO {
re.comboScope.Remove(3)
re.comboScope.Remove(2)
}
re.onVerbChanged()
if re.mode == DIALOG_MODE_NEW {
if re.nbSelected < 2 {
re.comboScope.SetActive(re.nbSelected)
} else {
re.comboScope.SetActive(0)
}
//re.titleSandbox.SetNoShowAll(true)
//re.titleSandbox.SetVisible(false)
//re.comboSandbox.SetNoShowAll(true)
//re.comboSandbox.SetVisible(false)
//re.comboSandbox.SetNoShowAll(true)
//re.comboSandbox.SetVisible(false)
re.comboSandbox.Append("", "")
for _, pn := range re.row.rl.app.ozProfiles {
re.comboSandbox.Append(pn, pn)
}
re.comboSandbox.SetActive(0)
re.btnPathChooser.SetCurrentFolder("/")
re.ok.SetSensitive(false)
re.onProtoChanged()
return
}
if r.Proto == "" {
re.comboProto.SetActiveID("any")
} else {
re.comboProto.SetActiveID(strings.ToLower(r.Proto))
}
re.comboSandbox.SetVisible(false)
re.comboSandbox.SetSensitive(false)
re.comboSandbox.SetNoShowAll(true)
if sgfw.RuleAction(r.Verb) == sgfw.RULE_ACTION_ALLOW || sgfw.RuleAction(r.Verb) == sgfw.RULE_ACTION_ALLOW_TLSONLY {
re.comboVerb.SetActiveID("allow")
} else {
re.comboVerb.SetActiveID("deny")
}
if sgfw.RuleAction(r.Verb) == sgfw.RULE_ACTION_ALLOW_TLSONLY {
re.checkTLS.SetActive(true)
}
if r.Sandbox == "" {
re.titleSandbox.SetNoShowAll(true)
re.titleSandbox.SetVisible(false)
re.labelSandbox.SetNoShowAll(true)
re.labelSandbox.SetVisible(false)
} else {
re.titleSandbox.SetVisible(true)
re.labelSandbox.SetNoShowAll(false)
re.labelSandbox.SetVisible(true)
re.labelSandbox.SetNoShowAll(false)
re.labelSandbox.SetText(r.Sandbox)
}
re.btnPathChooser.SetNoShowAll(true)
re.btnPathChooser.SetVisible(false)
re.btnPathChooser.SetSensitive(false)
re.entryPath.SetNoShowAll(false)
re.entryPath.SetVisible(true)
re.entryPath.SetText(r.Path)
target := strings.Split(r.Target, ":")
if len(target) != 2 {
return
}
re.hostEntry.SetText(target[0])
re.portEntry.SetText(target[1])
if r.UID > -1 {
re.comboUID.SetActiveID(strconv.FormatInt(int64(r.UID), 10))
}
if r.GID > -1 {
re.comboGID.SetActiveID(strconv.FormatInt(int64(r.GID), 10))
}
if re.mode == DIALOG_MODE_EDIT {
re.comboScope.SetVisible(false)
re.comboScope.SetNoShowAll(true)
re.comboScope.SetSensitive(false)
re.labelScope.SetNoShowAll(false)
re.labelScope.SetVisible(true)
re.labelScope.SetText(strings.Title(strings.ToLower(sgfw.RuleModeString[sgfw.RuleMode(r.Mode)])))
}
if re.mode == DIALOG_MODE_PROMPT || r.Mode == uint16(sgfw.RULE_MODE_PROCESS) {
re.titlePID.SetNoShowAll(false)
re.titlePID.SetVisible(true)
re.labelPID.SetNoShowAll(false)
re.labelPID.SetVisible(true)
pid := strconv.FormatUint(uint64(r.Pid), 10)
re.labelPID.SetText(pid)
}
if re.mode == DIALOG_MODE_SAVEAS {
re.comboScope.Remove(1)
re.comboScope.SetSensitive(false)
}
if re.mode == DIALOG_MODE_PROMPT {
re.entryOrigin.SetNoShowAll(false)
re.entryOrigin.SetVisible(true)
re.entryOrigin.SetSensitive(false)
re.entryOrigin.SetText(r.Origin)
re.labelOrigin.SetNoShowAll(false)
re.labelOrigin.SetVisible(true)
re.comboUID.SetSensitive(false)
re.comboGID.SetSensitive(false)
re.comboScope.SetActiveID(strconv.Itoa(int(sgfw.RuleModeValue[strings.ToUpper(re.row.rl.app.Config.DefaultAction)])))
re.checkUID.SetNoShowAll(false)
re.checkUID.SetVisible(true)
re.checkUID.SetSensitive(true)
re.checkGID.SetNoShowAll(false)
re.checkGID.SetVisible(true)
re.checkGID.SetSensitive(true)
re.comboVerb.SetNoShowAll(true)
re.comboVerb.SetVisible(false)
re.comboVerb.SetSensitive(false)
re.setPromptButtons()
ctv := r.IsSocks
if !ctv {
re.checkTLS.SetSensitive(false)
re.checkTLS.SetActive(false)
}
}
if re.mode == DIALOG_MODE_INFO {
re.comboScope.SetActiveID(strconv.Itoa(int(r.Mode)))
re.comboScope.SetSensitive(false)
re.comboVerb.SetSensitive(false)
re.hostEntry.SetSensitive(false)
re.portEntry.SetSensitive(false)
re.comboUID.SetSensitive(false)
re.comboGID.SetSensitive(false)
re.checkUID.SetSensitive(false)
re.checkGID.SetSensitive(false)
re.comboProto.SetSensitive(false)
re.checkTLS.SetSensitive(false)
re.ok.SetNoShowAll(true)
re.ok.SetSensitive(false)
re.ok.SetVisible(false)
}
re.onProtoChanged()
}
func (re *ruleNew) setPromptButtons() {
re.allow.SetNoShowAll(false)
re.allow.SetVisible(true)
re.allow.SetSensitive(true)
re.ok.SetLabel("_Deny")
}
func (re *ruleNew) toggleCheckTLS(val bool) {
if val && re.row.rule.IsSocks && re.mode != DIALOG_MODE_NEW && re.mode != DIALOG_MODE_INFO {
re.checkTLS.SetSensitive(true)
} else {
re.checkTLS.SetSensitive(false)
}
}
func (re *ruleNew) onProtoChanged() {
re.toggleCheckTLS( (re.comboProto.GetActiveID() == "tcp") )
if re.comboProto.GetActiveID() == "icmp" {
re.titlePort.SetText("Code:")
re.portEntry.SetPlaceholderText("Code")
} else {
re.titlePort.SetText("Port:")
re.portEntry.SetPlaceholderText("Port")
}
re.onChanged()
}
func (re *ruleNew) onVerbChanged() {
re.toggleCheckTLS( (re.comboVerb.GetActiveID() == "allow") )
}
func (re *ruleNew) validateFields() bool {
id := re.comboVerb.GetActiveID()
if id != "allow" && id != "allow_tls" && id != "deny" {
return false
}
proto := re.comboProto.GetActiveID()
protos := []string{"", "tcp", "udp", "icmp"}
found := false
for _, p := range protos {
if proto == p {
found = true
break
}
}
if !found {
return false
}
host, _ := re.hostEntry.GetText()
port, _ := re.portEntry.GetText()
if !isValidHost(host) {
return false
}
if !isValidPort(port, re.comboProto.GetActiveID()) {
return false
}
if re.mode == DIALOG_MODE_NEW {
fp := re.btnPathChooser.FileChooser.GetFilename()
if fp == "" || !isExecutableFile(fp) {
return false
}
}
return true
}
func isExecutableFile(file string) bool {
fi, _ := os.Stat(file)
fm := fi.Mode()
perm := uint32(fm.Perm())
return !( (perm&UserExecute == 0) && (perm&GroupExecute == 0) && (perm&OtherExecute == 0) )
}
func (re *ruleNew) onPortInsertText(entry *gtk.Entry, text string) {
current, _ := entry.GetText()
if current == "" && text == "*" {
return
}
if current == "*" {
entry.StopEmission("insert-text")
return
}
for _, c := range text {
if !unicode.IsDigit(c) {
entry.StopEmission("insert-text")
return
}
}
}
func (re *ruleNew) onChanged() {
valid := re.validateFields()
re.ok.SetSensitive(valid)
if re.mode == DIALOG_MODE_PROMPT {
re.allow.SetSensitive(valid)
}
}
func (re *ruleNew) onPathSet(btnChooser *gtk.FileChooserButton) {
fp := btnChooser.FileChooser.GetFilename()
if !isExecutableFile(fp) {
warnDialog(&re.row.rl.app.win.Window, "%s", "File not an executable!")
} else {
btnChooser.SetTooltipText(fp)
}
}
func (re *ruleNew) populateUID() {
for _, id := range re.row.rl.app.userIDs {
re.comboUID.Append(strconv.FormatInt(int64(id), 10), re.row.rl.app.userMap[id])
}
}
func (re *ruleNew) populateGID() {
for _, id := range re.row.rl.app.groupIDs {
re.comboGID.Append(strconv.FormatInt(int64(id), 10), re.row.rl.app.groupMap[id])
}
}

@ -3,91 +3,177 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"strconv"
"strings" "strings"
"sync"
"github.com/subgraph/fw-daemon/sgfw" "github.com/subgraph/fw-daemon/sgfw"
"github.com/gotk3/gotk3/gtk" "github.com/gotk3/gotk3/gtk"
"github.com/gotk3/gotk3/glib"
) )
type ruleList struct { type ruleList struct {
dbus *dbusObject lock *sync.Mutex
win *gtk.Window app *fwApp
mode sgfw.RuleMode
rows []*ruleRow
rules []sgfw.DbusRule
rowsByIndex map[int]*ruleRow
list *gtk.ListBox list *gtk.ListBox
col0 *gtk.SizeGroup
col1 *gtk.SizeGroup col1 *gtk.SizeGroup
col2 *gtk.SizeGroup col2 *gtk.SizeGroup
col3 *gtk.SizeGroup col3 *gtk.SizeGroup
col4 *gtk.SizeGroup raHandlerID glib.SignalHandle
col5 *gtk.SizeGroup
} }
type ruleRow struct { type ruleRow struct {
*gtk.ListBoxRow
rl *ruleList rl *ruleList
rule *sgfw.DbusRule rule *sgfw.DbusRule
widget *gtk.ListBoxRow gtkBox *gtk.Box
gtkSep *gtk.Separator
gtkGrid *gtk.Grid
gtkLabelApp *gtk.Label gtkLabelApp *gtk.Label
gtkLabelVerb *gtk.Label
gtkLabelOrigin *gtk.Label
gtkLabelPrivs *gtk.Label
gtkLabelTarget *gtk.Label gtkLabelTarget *gtk.Label
gtkButtonEdit *gtk.Button gtkButtonEdit *gtk.Button
gtkButtonSave *gtk.Button gtkButtonSave *gtk.Button
gtkButtonDelete *gtk.Button gtkButtonDelete *gtk.Button
gtkAppIcon *gtk.Image
gtkIconVerb *gtk.Image
} }
func newRuleList(dbus *dbusObject, win *gtk.Window, list *gtk.ListBox) *ruleList { func newRuleList(app *fwApp, list *gtk.ListBox, mode sgfw.RuleMode) *ruleList {
rl := &ruleList{dbus: dbus, win: win, list: list} rl := &ruleList{app: app, list: list}
rl.lock = new(sync.Mutex)
rl.mode = mode
rl.list.SetSelectionMode(gtk.SELECTION_NONE) rl.list.SetSelectionMode(gtk.SELECTION_NONE)
rl.col0, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col1, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL) rl.col1, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col2, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL) rl.col2, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col3, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL) rl.col3, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col4, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL) rl.list.SetActivateOnSingleClick(false)
rl.col5, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
return rl return rl
} }
func (rl *ruleList) loadRules(mode sgfw.RuleMode) error { func (rl *ruleList) loadRules(noAdd bool) error {
rules, err := rl.dbus.listRules() rl.lock.Lock()
defer rl.lock.Unlock()
rules, err := rl.app.Dbus.listRules()
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %+v\n", err) fmt.Fprintf(os.Stderr, "ERROR: %+v\n", err)
return err return err
} }
rl.addRules(rules, mode)
for i := (len(rules) - 1); i >= 0; i-- {
if sgfw.RuleMode(rules[i].Mode) != rl.mode {
rules = append(rules[:i], rules[i+1:]...)
}
}
rules = rl.sortRules(rules)
rl.rules = rules
if !noAdd {
rl.addRules(rules)
}
return nil return nil
} }
func (rl *ruleList) addRules(rules []sgfw.DbusRule, mode sgfw.RuleMode) { func (rl *ruleList) reloadRules(filter string) {
for i := 0; i < len(rules); i++ { rl.lock.Lock()
if sgfw.RuleMode(rules[i].Mode) != mode { defer rl.lock.Unlock()
continue filter = strings.ToLower(filter)
rules := make([]sgfw.DbusRule, len(rl.rules))
copy(rules, rl.rules)
if filter != "" {
for i := (len(rules) - 1); i >= 0; i-- {
if !strings.Contains(strings.ToLower(rules[i].Path), filter) && !strings.Contains(strings.ToLower(rules[i].Sandbox), filter) {
rules = append(rules[:i], rules[i+1:]...)
}
}
}
rules = rl.sortRules(rules)
for i, _ := range rl.rows {
rl.col0.RemoveWidget(rl.rows[i].gtkAppIcon)
rl.col1.RemoveWidget(rl.rows[i].gtkLabelApp)
rl.col2.RemoveWidget(rl.rows[i].gtkIconVerb)
rl.col3.RemoveWidget(rl.rows[i].gtkLabelTarget)
rl.rows[i].gtkLabelApp.Destroy()
rl.rows[i].gtkLabelApp = nil
rl.rows[i].gtkLabelTarget.Destroy()
rl.rows[i].gtkLabelTarget = nil
rl.rows[i].gtkButtonEdit.Destroy()
rl.rows[i].gtkButtonEdit = nil
rl.rows[i].gtkButtonSave.Destroy()
rl.rows[i].gtkButtonSave = nil
rl.rows[i].gtkButtonDelete.Destroy()
rl.rows[i].gtkButtonDelete = nil
rl.rows[i].gtkAppIcon.Destroy()
rl.rows[i].gtkAppIcon = nil
rl.rows[i].gtkIconVerb.Destroy()
rl.rows[i].gtkIconVerb = nil
rl.rows[i].gtkGrid.Destroy()
rl.rows[i].gtkGrid = nil
rl.rows[i].gtkSep.Destroy()
rl.rows[i].gtkSep = nil
rl.rows[i].gtkBox.Destroy()
rl.rows[i].gtkBox = nil
rl.list.Remove(rl.rows[i])
rl.rows[i].ListBoxRow.Destroy()
rl.rows[i].ListBoxRow = nil
//rl.rows[i].Destroy()
rl.rows[i].rule = nil
rl.rows[i].rl = nil
rl.rows[i] = nil
} }
row := createWidget(&rules[i]) rl.rows = rl.rows[:0]
row.rl = rl for i, _ := range rl.rowsByIndex {
delete(rl.rowsByIndex, i)
}
rules = rl.sortRules(rules)
rl.addRules(rules)
}
func (rl *ruleList) addRules(rules []sgfw.DbusRule) {
pi := 0
rl.rowsByIndex = make(map[int]*ruleRow, len(rules))
if rl.raHandlerID > 0 {
rl.list.HandlerDisconnect(rl.raHandlerID)
}
for i := 0; i < len(rules); i++ {
row := rl.createWidget(&rules[i])
rl.col0.AddWidget(row.gtkAppIcon)
rl.col1.AddWidget(row.gtkLabelApp) rl.col1.AddWidget(row.gtkLabelApp)
rl.col2.AddWidget(row.gtkLabelVerb) rl.col2.AddWidget(row.gtkIconVerb)
rl.col3.AddWidget(row.gtkLabelOrigin) rl.col3.AddWidget(row.gtkLabelTarget)
rl.col4.AddWidget(row.gtkLabelPrivs) rl.list.Add(row)
rl.col5.AddWidget(row.gtkLabelTarget) rl.rowsByIndex[row.GetIndex()] = row
rl.list.Add(row.widget) row.ShowAll()
if i > 0 && rules[pi].Path == rules[i].Path && rules[pi].Sandbox == rules[i].Sandbox {
row.hideTitle()
}
rl.rows = append(rl.rows, row)
pi = i
} }
rl.raHandlerID, _ = rl.list.Connect("row-activated", rl.showInformation)
} }
func createWidget(rule *sgfw.DbusRule) *ruleRow { func (rl *ruleList) createWidget(rule *sgfw.DbusRule) *ruleRow {
row := &ruleRow{} row := &ruleRow{rl: rl}
row.rule = rule row.rule = rule
builder := newBuilder("RuleItem") builder := newBuilder("RuleItem")
var grid *gtk.Grid
builder.getItems( builder.getItems(
"grid", &grid, "grid", &row.gtkGrid,
"app_label", &row.gtkLabelApp, "app_label", &row.gtkLabelApp,
"verb_label", &row.gtkLabelVerb, "verb_icon", &row.gtkIconVerb,
"origin_label", &row.gtkLabelOrigin,
"privs_label", &row.gtkLabelPrivs,
"target_label", &row.gtkLabelTarget, "target_label", &row.gtkLabelTarget,
"edit_button", &row.gtkButtonEdit, "edit_button", &row.gtkButtonEdit,
"save_button", &row.gtkButtonSave, "save_button", &row.gtkButtonSave,
"delete_button", &row.gtkButtonDelete, "delete_button", &row.gtkButtonDelete,
"app_icon", &row.gtkAppIcon,
) )
switch sgfw.RuleMode(rule.Mode) { switch sgfw.RuleMode(rule.Mode) {
case sgfw.RULE_MODE_SYSTEM: case sgfw.RULE_MODE_SYSTEM:
@ -96,6 +182,10 @@ func createWidget(rule *sgfw.DbusRule) *ruleRow {
row.gtkButtonDelete.SetSensitive(false) row.gtkButtonDelete.SetSensitive(false)
row.gtkButtonDelete.SetTooltipText("Cannot delete system rules") row.gtkButtonDelete.SetTooltipText("Cannot delete system rules")
break break
case sgfw.RULE_MODE_PROCESS:
row.gtkButtonSave.SetSensitive(true)
row.gtkButtonSave.SetNoShowAll(false)
break
case sgfw.RULE_MODE_SESSION: case sgfw.RULE_MODE_SESSION:
row.gtkButtonSave.SetSensitive(true) row.gtkButtonSave.SetSensitive(true)
row.gtkButtonSave.SetNoShowAll(false) row.gtkButtonSave.SetNoShowAll(false)
@ -107,28 +197,104 @@ func createWidget(rule *sgfw.DbusRule) *ruleRow {
"on_save_rule": row.onSaveAsNew, "on_save_rule": row.onSaveAsNew,
"on_delete_rule": row.onDelete, "on_delete_rule": row.onDelete,
}) })
row.widget, _ = gtk.ListBoxRowNew() row.gtkBox, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
row.widget.Add(grid) row.gtkSep, _ = gtk.SeparatorNew(gtk.ORIENTATION_HORIZONTAL)
row.ListBoxRow, _ = gtk.ListBoxRowNew()
row.gtkBox.Add(row.gtkGrid)
row.gtkBox.Add(row.gtkSep)
row.Add(row.gtkBox)
row.SetProperty("selectable", false)
row.SetProperty("activatable", true)
row.showTitle()
row.update() row.update()
//builder.Object.Unref()
builder = nil
return row return row
} }
func (rl *ruleList) showInformation(list *gtk.ListBox, row *gtk.ListBoxRow) bool {
rr := rl.rowsByIndex[row.GetIndex()]
rr.runNewEditor(DIALOG_MODE_INFO)
return true
}
func (rr *ruleRow) update() { func (rr *ruleRow) update() {
if rr.rule.Mode == uint16(sgfw.RULE_MODE_PROCESS) { rr.gtkLabelApp.SetTooltipText(rr.rule.Path)
appstr := "(" + strconv.Itoa(int(rr.rule.Pid)) + ") " + rr.rule.App rr.setVerbIcon()
rr.gtkLabelApp.SetText(appstr) tt := getTargetText(rr.rule)
if rr.rule.UID > -1 || rr.rule.GID > -1 {
tt = tt + " for "
}
if rr.rule.UID > -1 {
tt = tt + rr.rl.app.LookupUsername(rr.rule.UID)
}
if rr.rule.UID > -1 && rr.rule.GID > -1 {
tt = tt + ":"
}
if rr.rule.GID > -1 {
tt = tt + rr.rl.app.LookupGroup(rr.rule.GID)
}
rr.gtkLabelTarget.SetText(tt)
}
func (rr *ruleRow) hideTitle() {
rr.gtkLabelApp.SetText("")
rr.gtkAppIcon.Clear()
}
func (rr *ruleRow) showTitle() {
in := []string{rr.rule.App}
if rr.rule.Sandbox != "" {
in = append([]string{rr.rule.Sandbox}, in...)
}
if rr.rule.App == "[unknown]" {
in = []string{"image-missing"}
}
it, err := gtk.IconThemeGetDefault()
if err != nil {
fmt.Println("Error getting icon theme.")
} else { } else {
found := false
for _, ia := range in {
pb, _ := it.LoadIcon(ia, int(gtk.ICON_SIZE_BUTTON), gtk.ICON_LOOKUP_USE_BUILTIN)
if pb != nil {
rr.gtkAppIcon.SetFromIconName(ia, gtk.ICON_SIZE_BUTTON)
found = true
break
}
}
if !found {
rr.gtkAppIcon.SetFromIconName("terminal", gtk.ICON_SIZE_BUTTON)
}
}
rr.gtkLabelApp.SetText(rr.rule.App) rr.gtkLabelApp.SetText(rr.rule.App)
}
func (rr *ruleRow) setVerbIcon() {
it, err := gtk.IconThemeGetDefault()
in := ""
tt := ""
if sgfw.RuleAction(rr.rule.Verb) == sgfw.RULE_ACTION_DENY {
in = "gtk-no"
tt = "Deny"
} else if sgfw.RuleAction(rr.rule.Verb) == sgfw.RULE_ACTION_ALLOW {
in = "gtk-yes"
tt = "Allow"
} else if sgfw.RuleAction(rr.rule.Verb) == sgfw.RULE_ACTION_ALLOW_TLSONLY {
in = "gtk-yes"
tt = "Allow TLS"
} }
rr.gtkLabelApp.SetTooltipText(rr.rule.Path) if err != nil {
rr.gtkLabelVerb.SetText(getVerbText(rr.rule)) fmt.Println("Error getting icon theme.")
if rr.rule.Proto == "tcp" { return
rr.gtkLabelOrigin.SetText(rr.rule.Origin) }
} else { pb, _ := it.LoadIcon(in, int(gtk.ICON_SIZE_BUTTON), gtk.ICON_LOOKUP_USE_BUILTIN)
rr.gtkLabelOrigin.SetText(rr.rule.Origin + " (" + rr.rule.Proto + ")") if pb == nil {
fmt.Println("Error getting icon theme.")
return
} }
rr.gtkLabelPrivs.SetText(rr.rule.Privs) rr.gtkIconVerb.SetFromIconName(in, gtk.ICON_SIZE_BUTTON)
rr.gtkLabelTarget.SetText(getTargetText(rr.rule)) rr.gtkIconVerb.SetTooltipText(tt)
} }
func getVerbText(rule *sgfw.DbusRule) string { func getVerbText(rule *sgfw.DbusRule) string {
@ -142,44 +308,73 @@ func getVerbText(rule *sgfw.DbusRule) string {
} }
func getTargetText(rule *sgfw.DbusRule) string { func getTargetText(rule *sgfw.DbusRule) string {
verb := "Deny"
if sgfw.RuleAction(rule.Verb) == sgfw.RULE_ACTION_ALLOW || sgfw.RuleAction(rule.Verb) == sgfw.RULE_ACTION_ALLOW_TLSONLY {
verb = "Allow"
}
if rule.Target == "*:*" { if rule.Target == "*:*" {
return "All connections" ct := "any"
if sgfw.RuleAction(rule.Verb) == sgfw.RULE_ACTION_DENY {
ct = "all"
}
res := []string{verb, ct, "connections"}
if sgfw.RuleAction(rule.Verb) == sgfw.RULE_ACTION_ALLOW_TLSONLY {
res = append(res, "with TLS")
}
return strings.Join(res, " ")
} }
items := strings.Split(rule.Target, ":")
items := strings.Split(rule.Target, ":")
if len(items) != 2 { if len(items) != 2 {
return rule.Target return strings.Join([]string{verb, rule.Target}, " ")
} }
ct := "connections"
if rule.Proto != "tcp" {
ct = "data"
}
target := []string{verb, strings.ToUpper(rule.Proto), ct}
if sgfw.RuleAction(rule.Verb) == sgfw.RULE_ACTION_ALLOW_TLSONLY {
target = append(target, "with TLS")
}
if rule.Origin != "" {
target = append(target, "from ", rule.Origin)
}
if items[0] == "*" { if items[0] == "*" {
if rule.Proto == "tcp" { if rule.Proto == "tcp" {
return fmt.Sprintf("Connections to ALL hosts on port %s", items[1]) target = append(target, fmt.Sprintf("to ALL hosts on port %s", items[1]))
} else if rule.Proto == "icmp" { } else if rule.Proto == "icmp" {
return fmt.Sprintf("Data to ALL hosts with ICMP code %s", items[1]) target = append(target, fmt.Sprintf("to ALL hosts with code %s", items[1]))
} else {
target = append(target, fmt.Sprintf("to ALL hosts on port %s", items[1]))
} }
return fmt.Sprintf("Data to ALL hosts on port %s", items[1]) return strings.Join(target, " ")
} }
if items[1] == "*" { if items[1] == "*" {
if rule.Proto == "tcp" { if rule.Proto == "tcp" {
return fmt.Sprintf("All connections to host %s", items[0]) target = append(target, fmt.Sprintf("to host %s", items[0]))
} else if rule.Proto == "icmp" {
target = append(target, fmt.Sprintf("to host %s", items[0]))
} else {
target = append(target, fmt.Sprintf("to host %s", items[0]))
} }
return fmt.Sprintf("All data to host %s", items[0]) return strings.Join(target, " ")
} }
ps := "port"
if rule.Proto == "tcp" { if rule.Proto == "icmp" {
return fmt.Sprintf("Connections to %s on port %s", items[0], items[1]) ps = "code"
} else if rule.Proto == "icmp" {
return fmt.Sprintf("Data to %s with ICMP code %s", items[0], items[1])
} }
return fmt.Sprintf("Data to %s on port %s", items[0], items[1]) target = append(target, fmt.Sprintf("to %s on %s %s", items[0], ps, items[1]))
return strings.Join(target, " ")
} }
func (rr *ruleRow) onSaveAsNew() { func (rr *ruleRow) onSaveAsNew() {
rr.runEditor(true) rr.runNewEditor(DIALOG_MODE_SAVEAS)
} }
func (rr *ruleRow) onEdit() { func (rr *ruleRow) onEdit() {
rr.runEditor(false) rr.runNewEditor(DIALOG_MODE_EDIT)
} }
func (rr *ruleRow) onDelete() { func (rr *ruleRow) onDelete() {
@ -187,22 +382,22 @@ func (rr *ruleRow) onDelete() {
if rr.rule.Sandbox != "" { if rr.rule.Sandbox != "" {
ss := `Are you sure you want to delete this rule: ss := `Are you sure you want to delete this rule:
<b>Path:</b> %s <b>Path:</b> %s
<b>Sandbox:</b> %s <b>Sandbox:</b> %s
<b>Rule:</b> %s %s` <b>Rule:</b> %s`
body = fmt.Sprintf(ss, rr.rule.Path, rr.rule.Sandbox, getVerbText(rr.rule), getTargetText(rr.rule)) body = fmt.Sprintf(ss, rr.rule.Path, rr.rule.Sandbox, getTargetText(rr.rule))
} else { } else {
ss := `Are you sure you want to delete this rule: ss := `Are you sure you want to delete this rule:
<b>Path:</b> %s <b>Path:</b> %s
<b>Rule:</b> %s %s` <b>Rule:</b> %s`
body = fmt.Sprintf(ss, rr.rule.Path, getVerbText(rr.rule), getTargetText(rr.rule)) body = fmt.Sprintf(ss, rr.rule.Path, getTargetText(rr.rule))
} }
d := gtk.MessageDialogNewWithMarkup( d := gtk.MessageDialogNewWithMarkup(
rr.rl.win, rr.rl.app.win,
gtk.DIALOG_DESTROY_WITH_PARENT, gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION, gtk.MESSAGE_QUESTION,
gtk.BUTTONS_OK_CANCEL, gtk.BUTTONS_OK_CANCEL,
@ -212,19 +407,37 @@ func (rr *ruleRow) onDelete() {
rr.delete() rr.delete()
} }
d.Destroy() d.Destroy()
} }
func (rl *ruleList) remove(rr *ruleRow) { func (rl *ruleList) remove(rr *ruleRow) {
rl.col0.RemoveWidget(rr.gtkAppIcon)
rl.col1.RemoveWidget(rr.gtkLabelApp) rl.col1.RemoveWidget(rr.gtkLabelApp)
rl.col2.RemoveWidget(rr.gtkLabelVerb) rl.col2.RemoveWidget(rr.gtkIconVerb)
rl.col3.RemoveWidget(rr.gtkLabelOrigin) rl.col3.RemoveWidget(rr.gtkLabelTarget)
rl.col4.RemoveWidget(rr.gtkLabelPrivs) rl.list.Remove(rr.ListBoxRow)
rl.col5.RemoveWidget(rr.gtkLabelTarget) for i := (len(rl.rules) - 1); i >= 0; i-- {
rl.list.Remove(rr.widget) if *rr.rule == rl.rules[i] {
rl.rules = append(rl.rules[:i], rl.rules[i+1:]...)
break;
}
}
} }
func (rr *ruleRow) delete() { func (rr *ruleRow) delete() {
idx := rr.ListBoxRow.GetIndex()
ndx := idx + 1
pdx := idx - 1
if ndx < len(rr.rl.rows) {
if pdx != -1 {
if rr.rl.rows[pdx].rule.Path != rr.rule.Path || rr.rl.rows[pdx].rule.Sandbox != rr.rule.Sandbox {
rr.rl.rows[ndx].showTitle()
}
} else {
rr.rl.rows[ndx].showTitle()
}
}
rr.rl.remove(rr) rr.rl.remove(rr)
rr.rl.dbus.deleteRule(rr.rule.ID) rr.rl.app.Dbus.deleteRule(rr.rule.ID)
rr.rl.rows = append(rr.rl.rows[:idx], rr.rl.rows[idx+1:]...)
} }

@ -0,0 +1,17 @@
.PHONY: generate
all: generate
gschemas.compiled: *.xml
glib-compile-schemas .
schemas.go: gschemas.compiled
ruby ./generate.rb
generate: touch schemas.go
touch:
ls *.xml | xargs -n1 touch
doctor: touch generate
git diff --exit-code .

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<schemalist>
<schema id="com.subgraph.Firewall.Settings">
<key name="window-height" type="u">
<default>0</default>
<summary>Main window height</summary>
<description>Preserved height of the main window</description>
</key>
<key name="window-width" type="u">
<default>0</default>
<summary>Main window width</summary>
<description>Preserved width of the main window</description>
</key>
<key name="window-top" type="u">
<default>0</default>
<summary>Main window top</summary>
<description>Preserved top coordinate of the main window</description>
</key>
<key name="window-left" type="u">
<default>0</default>
<summary>Main window left</summary>
<description>Preserved left coordinate of the main window</description>
</key>
<key name="prompt-toplevel" type="b">
<default>true</default>
<summary>GTK Prompt As Top Level</summary>
<description>Bring the prompt window as front most and sticky on new prompt requests</description>
</key>
</schema>
</schemalist>

@ -0,0 +1,23 @@
package definitions
import (
"encoding/hex"
"io/ioutil"
"path"
)
func fileContent() []byte {
decoded, _ := hex.DecodeString(schemaDefinition)
return decoded
}
func writeSchemaToDir(dir string) {
ioutil.WriteFile(path.Join(dir, "gschemas.compiled"), fileContent(), 0664)
}
// SchemaInTempDir will create a new temporary directory and put the gsettings schema file in there. It is the callers responsibility to remove the directory
func SchemaInTempDir() string {
dir, _ := ioutil.TempDir("", "fw-settings-gschema")
writeSchemaToDir(dir)
return dir
}

@ -0,0 +1,19 @@
#!/usr/bin/env ruby
def gen_go_file
binary_definition = File.binread("gschemas.compiled")
hex = binary_definition.each_byte.map { |b| "%02x" % b }.join
File.open("schemas.go", "w") do |f|
sliced = hex.chars.each_slice(80).map{ |s| s.join }.join "\"+\n\t\""
f.puts <<TEMPLATE
package definitions
const schemaDefinition = ""+
\t"#{sliced}"
TEMPLATE
end
end
gen_go_file

@ -0,0 +1,14 @@
package definitions
const schemaDefinition = ""+
"4756617269616e740000000000000000180000005800000000000028020000000000000000000000"+
"05150000ffffffff5800000000004c00580000005c00000091f48b72000000005c0000001e004800"+
"7c0000002c01000001000000636f6d2e73756267726170682e4669726577616c6c2e53657474696e"+
"67730000000000280600000000000000000000000200000003000000040000000400000055c38862"+
"040000002c0100000b00760038010000400100001d8e1b7f04000000400100000a00760050010000"+
"58010000ea646ab404000000580100000c0076006801000070010000c3c5731e0400000070010000"+
"0d007600800100008801000005150000ffffffff8801000000004c00880100009c0100007ffe7dcf"+
"040000009c0100000f007600b0010000b501000077696e646f772d6c656674000000000000287529"+
"77696e646f772d746f70000000000000000000000028752977696e646f772d776964746800000000"+
"000000000028752977696e646f772d68656967687400000000000000002875290500000003000000"+
"00000000010000000200000070726f6d70742d746f706c6576656c00000000000100286229"

@ -0,0 +1,129 @@
package settings
import (
"os"
"github.com/subgraph/fw-daemon/fw-settings/settings/definitions"
"github.com/gotk3/gotk3/glib"
)
var cachedSchema *glib.SettingsSchemaSource
func getSchemaSource() *glib.SettingsSchemaSource {
if cachedSchema == nil {
dir := definitions.SchemaInTempDir()
defer os.Remove(dir)
cachedSchema = glib.SettingsSchemaSourceNewFromDirectory(dir, nil, true)
}
return cachedSchema
}
func getSchema() *glib.SettingsSchema {
return getSchemaSource().Lookup("com.subgraph.Firewall.Settings", false)
}
func getDefaultSettings() *glib.Settings {
return glib.SettingsNewFull(getSchema(), nil, "/com/subgraph/firewall/settings/")
}
type Settings struct {
def, spec *glib.Settings
}
func Init() *Settings {
s := &Settings{}
s.def = getDefaultSettings()
return s
}
func (s *Settings) settingsForGet(name string) *glib.Settings {
if s.spec != nil {
return s.spec
}
return s.def
}
func (s *Settings) settingsForSet() *glib.Settings {
if s.spec != nil {
return s.spec
}
return s.def
}
func (s *Settings) getBooleanSetting(name string) bool {
return s.settingsForGet(name).GetBoolean(name)
}
func (s *Settings) setBooleanSetting(name string, val bool) {
sets := s.settingsForSet()
sets.SetBoolean(name, val)
}
func (s *Settings) getIntegerSetting(name string) int {
return s.settingsForGet(name).GetInt(name)
}
func (s *Settings) setIntegerSetting(name string, val int) {
sets := s.settingsForSet()
sets.SetInt(name, val)
}
func (s *Settings) getUIntegerSetting(name string) uint {
return s.settingsForGet(name).GetUInt(name)
}
func (s *Settings) setUIntegerSetting(name string, val uint) {
sets := s.settingsForSet()
sets.SetUInt(name, val)
}
func (s *Settings) getStringSetting(name string) string {
return s.settingsForGet(name).GetString(name)
}
func (s *Settings) setStringSetting(name string, val string) {
sets := s.settingsForSet()
sets.SetString(name, val)
}
func (s *Settings) GetWindowHeight() uint {
return s.getUIntegerSetting("window-height")
}
func (s *Settings) SetWindowHeight(v uint) {
s.setUIntegerSetting("window-height", v)
}
func (s *Settings) GetWindowWidth() uint {
return s.getUIntegerSetting("window-width")
}
func (s *Settings) SetWindowWidth(v uint) {
s.setUIntegerSetting("window-width", v)
}
func (s *Settings) GetWindowTop() uint {
return s.getUIntegerSetting("window-top")
}
func (s *Settings) SetWindowTop(v uint) {
s.setUIntegerSetting("window-top", v)
}
func (s *Settings) GetWindowLeft() uint {
return s.getUIntegerSetting("window-left")
}
func (s *Settings) SetWindowLeft(v uint) {
s.setUIntegerSetting("window-left", v)
}
func (s *Settings) GetToplevelPrompt() bool {
return s.getBooleanSetting("prompt-toplevel")
}
func (s *Settings) SetToplevelPrompt(v bool) {
s.setBooleanSetting("prompt-toplevel", v)
}

@ -0,0 +1,29 @@
// +build go1.8
package main
import (
"sort"
"github.com/subgraph/fw-daemon/sgfw"
)
func (rl *ruleList) sortRules(rules []sgfw.DbusRule) []sgfw.DbusRule {
sort.SliceStable(rules, func(i, j int) bool {
//sgfw.RuleActionString[sgfw.RuleAction(rules[i].Verb)]
//sgfw.RuleActionString[sgfw.RuleAction(rules[j].Verb)]
in := rules[i].Sandbox + rules[i].App + rules[i].Target
jn := rules[j].Sandbox + rules[j].App + rules[j].Target
order := []string{in,jn}
sort.Strings(order)
if rules[i].App == rules[j].App && rules[i].Sandbox == rules[j].Sandbox {
if sgfw.RuleAction(rules[i].Verb) == sgfw.RULE_ACTION_DENY || sgfw.RuleAction(rules[j].Verb) == sgfw.RULE_ACTION_DENY {
if rules[i].Verb != rules[j].Verb {
return (sgfw.RuleAction(rules[i].Verb) == sgfw.RULE_ACTION_DENY)
}
}
}
return (order[0] == in)
})
return rules
}

@ -0,0 +1,160 @@
// +build !go1.8
package main
import (
"sort"
"reflect"
"github.com/subgraph/fw-daemon/sgfw"
)
// Copied from golang 1.8 sources
// START sort.SliceStable
type lessSwap struct {
Less func(i, j int) bool
Swap func(i, j int)
}
func SliceStable(slice interface{}, less func(i, j int) bool) {
rv := reflect.ValueOf(slice)
swap := reflect.Swapper(slice)
stable_func(lessSwap{less, swap}, rv.Len())
}
func symMerge_func(data lessSwap, a, m, b int) {
if m-a == 1 {
i := m
j := b
for i < j {
h := int(uint(i+j) >> 1)
if data.Less(h, a) {
i = h + 1
} else {
j = h
}
}
for k := a; k < i-1; k++ {
data.Swap(k, k+1)
}
return
}
if b-m == 1 {
i := a
j := m
for i < j {
h := int(uint(i+j) >> 1)
if !data.Less(m, h) {
i = h + 1
} else {
j = h
}
}
for k := m; k > i; k-- {
data.Swap(k, k-1)
}
return
}
mid := int(uint(a+b) >> 1)
n := mid + m
var start, r int
if m > mid {
start = n - b
r = mid
} else {
start = a
r = m
}
p := n - 1
for start < r {
c := int(uint(start+r) >> 1)
if !data.Less(p-c, c) {
start = c + 1
} else {
r = c
}
}
end := n - start
if start < m && m < end {
rotate_func(data, start, m, end)
}
if a < start && start < mid {
symMerge_func(data, a, start, mid)
}
if mid < end && end < b {
symMerge_func(data, mid, end, b)
}
}
func swapRange_func(data lessSwap, a, b, n int) {
for i := 0; i < n; i++ {
data.Swap(a+i, b+i)
}
}
func rotate_func(data lessSwap, a, m, b int) {
i := m - a
j := b - m
for i != j {
if i > j {
swapRange_func(data, m-i, m, j)
i -= j
} else {
swapRange_func(data, m-i, m+j-i, i)
j -= i
}
}
swapRange_func(data, m-i, m, i)
}
func insertionSort_func(data lessSwap, a, b int) {
for i := a + 1; i < b; i++ {
for j := i; j > a && data.Less(j, j-1); j-- {
data.Swap(j, j-1)
}
}
}
// Auto-generated variant of sort.go:stable
func stable_func(data lessSwap, n int) {
blockSize := 20
a, b := 0, blockSize
for b <= n {
insertionSort_func(data, a, b)
a = b
b += blockSize
}
insertionSort_func(data, a, n)
for blockSize < n {
a, b = 0, 2*blockSize
for b <= n {
symMerge_func(data, a, a+blockSize, b)
a = b
b += 2 * blockSize
}
if m := a + blockSize; m < n {
symMerge_func(data, a, m, n)
}
blockSize *= 2
}
}
// END sort.SliceStable
func (rl *ruleList) sortRules(rules []sgfw.DbusRule) []sgfw.DbusRule {
SliceStable(rules, func(i, j int) bool {
in := rules[i].Sandbox + rules[i].App + rules[i].Target
jn := rules[j].Sandbox + rules[j].App + rules[j].Target
order := []string{in,jn}
sort.Strings(order)
if rules[i].App == rules[j].App && rules[i].Sandbox == rules[j].Sandbox {
if sgfw.RuleAction(rules[i].Verb) == sgfw.RULE_ACTION_DENY || sgfw.RuleAction(rules[j].Verb) == sgfw.RULE_ACTION_DENY {
if rules[i].Verb != rules[j].Verb {
return (sgfw.RuleAction(rules[i].Verb) == sgfw.RULE_ACTION_DENY)
}
}
}
return (order[0] == in)
})
return rules
}

@ -6,14 +6,16 @@ const Pango = imports.gi.Pango;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const CheckBox = imports.ui.checkBox
const ModalDialog = imports.ui.modalDialog; const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const RuleScope = { const RuleScope = {
APPLY_ONCE: 0, APPLY_SESSION: 0,
APPLY_SESSION: 1, APPLY_PROCESS: 1,
APPLY_PROCESS: 2, APPLY_PERMANENT: 2,
APPLY_FOREVER: 3, APPLY_SYSTEM: 3,
APPLY_ONCE: 4,
}; };
const DetailSection = new Lang.Class({ const DetailSection = new Lang.Class({
@ -22,7 +24,7 @@ const DetailSection = new Lang.Class({
_init: function(sandboxed) { _init: function(sandboxed) {
this.actor = new St.BoxLayout({ style_class: 'fw-details-section' }); this.actor = new St.BoxLayout({ style_class: 'fw-details-section' });
this._left = new St.BoxLayout({ vertical: true, style_class: 'fw-details-left'}); this._left = new St.BoxLayout({ vertical: true, style_class: 'fw-details-left'});
this._right = new St.BoxLayout({ vertical: true }); this._right = new St.BoxLayout({ vertical: true, style_class: 'fw-details-right' });
this.actor.add_child(this._left); this.actor.add_child(this._left);
this.actor.add_child(this._right); this.actor.add_child(this._right);
@ -30,8 +32,8 @@ const DetailSection = new Lang.Class({
this.path = this._addDetails("Path:"); this.path = this._addDetails("Path:");
this.pid = this._addDetails("Process ID:"); this.pid = this._addDetails("Process ID:");
this.origin = this._addDetails("Origin:"); this.origin = this._addDetails("Origin:");
this.user = this._addDetails("User:"); this.user = this._addCheckboxDetails("User:");
this.group = this._addDetails("Group:"); this.group = this._addCheckboxDetails("Group:");
this.sandboxed = sandboxed; this.sandboxed = sandboxed;
if (sandboxed) { if (sandboxed) {
@ -40,15 +42,43 @@ const DetailSection = new Lang.Class({
this.optstring = this._addDetails(""); this.optstring = this._addDetails("");
}, },
_addDetails: function(text) { _addDetails: function(text, d) {
let title = new St.Label({ style_class: 'fw-detail-title', text: text}); let title = new St.Label({ style_class: 'fw-detail-title', text: text});
let msg = new St.Label({ style_class: 'fw-detail-message' });
this._left.add(title, { expand: true, x_fill: false, x_align: St.Align.END}); this._left.add(title, { expand: true, x_fill: false, x_align: St.Align.END});
let msg = new St.Label({ style_class: 'fw-detail-message' });
if (d === undefined) {
this._right.add(msg); this._right.add(msg);
} else {
let inner = new St.BoxLayout({ vertical: false, style_class: 'fw-ugid-apply-checkbox' });
inner.add(msg);
inner.add(d.actor);
this._right.add(inner);
}
return msg; return msg;
}, },
setDetails: function(ip, path, pid, uid, gid, user, group, origin, proto, optstring, sandbox) { _addCheckboxDetails: function(text) {
let title = new St.Label({ style_class: 'fw-detail-title', text: text});
title.hide();
this._left.add(title, { expand: true, x_fill: false, x_align: St.Align.END});
//let msg = new St.Label({ style_class: 'fw-detail-message' });
let check = new CheckBox.CheckBox("");
check.actor.checked = true;
check.actor.hide();
this._right.add(check.actor);
/*
let inner = new St.BoxLayout({ vertical: false, style_class: 'fw-ugid-apply-checkbox' });
inner.add(msg);
inner.add(check.actor);
this._right.add(inner);
*/
return [title, check];
},
setDetails: function(ip, path, pid, uid, gid, user, group, origin, proto, optstring, sandbox, expert) {
this.ipAddr.text = ip; this.ipAddr.text = ip;
this.path.text = path; this.path.text = path;
@ -60,22 +90,28 @@ const DetailSection = new Lang.Class({
this.origin.text = origin; this.origin.text = origin;
if (expert === true) {
this.user[0].show();
this.user[1].actor.show();
if (user != "") { if (user != "") {
this.user.text = user; this.user[1].getLabelActor().text = user;
if (uid != -1) { if (uid != -1) {
this.user.text += " (" + uid.toString() + ")"; this.user[1].getLabelActor().text += " (" + uid.toString() + ")";
} }
} else { } else {
this.user.text = "uid:" + uid.toString(); this.user[1].getLabelActor().text = "uid:" + uid.toString();
} }
this.group[0].show();
this.group[1].actor.show();
if (group != "") { if (group != "") {
this.group.text = group; this.group[1].getLabelActor().text = group;
if (gid != -1) { if (gid != -1) {
this.group.text += " (" + gid.toString() + ")"; this.group[1].getLabelActor().text += " (" + gid.toString() + ")";
} }
} else { } else {
this.group.text = "gid:" + gid.toString(); this.group[1].getLabelActor().text = "gid:" + gid.toString();
}
} }
if (sandbox != "") { if (sandbox != "") {
@ -142,9 +178,9 @@ const OptionList = new Lang.Class({
_init: function(pid_known, sandboxed) { _init: function(pid_known, sandboxed) {
this.actor = new St.BoxLayout({vertical: true, style_class: 'fw-option-list'}); this.actor = new St.BoxLayout({vertical: true, style_class: 'fw-option-list'});
if (pid_known) { if (pid_known) {
this.buttonGroup = new ButtonGroup("Forever", "Session", "Once", "PID"); this.buttonGroup = new ButtonGroup("Permanent", "Session", "Process", "Once");
} else { } else {
this.buttonGroup = new ButtonGroup("Forever", "Session", "Once"); this.buttonGroup = new ButtonGroup("Permanent", "Session", "Once");
} }
this.actor.add_child(this.buttonGroup.actor); this.actor.add_child(this.buttonGroup.actor);
this.items = []; this.items = [];
@ -224,13 +260,13 @@ const OptionList = new Lang.Class({
selectedScope: function() { selectedScope: function() {
switch(this.buttonGroup._checked) { switch(this.buttonGroup._checked) {
case 0: case 0:
return RuleScope.APPLY_FOREVER; return RuleScope.APPLY_PERMANENT;
case 1: case 1:
return RuleScope.APPLY_SESSION; return RuleScope.APPLY_SESSION;
case 2: case 2:
return RuleScope.APPLY_ONCE;
case 3:
return RuleScope.APPLY_PROCESS; return RuleScope.APPLY_PROCESS;
case 3:
return RuleScope.APPLY_ONCE;
default: default:
log("SGFW: unexpected scope value "+ this.buttonGroup._selected); log("SGFW: unexpected scope value "+ this.buttonGroup._selected);
return RuleScope.APPLY_SESSION; return RuleScope.APPLY_SESSION;
@ -238,14 +274,17 @@ const OptionList = new Lang.Class({
}, },
scopeToIdx: function(scope) { scopeToIdx: function(scope) {
if (scope === undefined) {
scope = this.buttonGroup._checked;
}
switch (scope) { switch (scope) {
case RuleScope.APPLY_PROCESS:
return 3;
case RuleScope.APPLY_ONCE: case RuleScope.APPLY_ONCE:
return 3;
case RuleScope.APPLY_PROCESS:
return 2; return 2;
case RuleScope.APPLY_SESSION: case RuleScope.APPLY_SESSION:
return 1; return 1;
case RuleScope.APPLY_FOREVER: case RuleScope.APPLY_PERMANENT:
return 0; return 0;
default: default:
log("SGFW: unexpected scope value "+ scope); log("SGFW: unexpected scope value "+ scope);
@ -253,6 +292,27 @@ const OptionList = new Lang.Class({
} }
}, },
scopeToString: function(scope) {
if (scope === undefined) {
scope = this.buttonGroup._checked;
}
switch (this.selectedScope(scope)) {
case RuleScope.APPLY_PROCESS:
return "PROCESS";
case RuleScope.APPLY_ONCE:
return "ONCE";
case RuleScope.APPLY_SESSION:
return "SESSION";
case RuleScope.APPLY_PERMANENT:
return "PERMANENT";
case RuleScope.APPLY_SYSTEM:
return "SYSTEM";
default:
log("SGFW: unexpected scope value "+ scope);
return "SESSION";
}
},
scopeNext: function() { scopeNext: function() {
this.buttonGroup.next(); this.buttonGroup.next();
}, },
@ -484,12 +544,22 @@ const PromptDialogHeader = new Lang.Class({
_init: function() { _init: function() {
this.actor = new St.BoxLayout(); this.actor = new St.BoxLayout();
let inner = new St.BoxLayout({ vertical: true }); let inner = new St.BoxLayout({ vertical: true, x_expand: true });
this.icon = new St.Icon({style_class: 'fw-prompt-icon'}) this.icon = new St.Icon({style_class: 'fw-prompt-icon'})
this.title = new St.Label({style_class: 'fw-prompt-title'}) this.title = new St.Label({style_class: 'fw-prompt-title'})
this.message = new St.Label({style_class: 'fw-prompt-message'}); this.message = new St.Label({style_class: 'fw-prompt-message'});
this.message.clutter_text.line_wrap = true; this.message.clutter_text.line_wrap = true;
this.message.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this.message.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.wb = new St.BoxLayout({x_expand: true});
let spacer = new St.Bin();
this.wb.add(spacer, {expand: true});
this.waiting = new St.Label({style_class: 'fw-prompt-waiting'});
this.waiting.text = "";
this.wb.add_child(this.waiting);
inner.add_child(this.wb);
inner.add_child(this.title); inner.add_child(this.title);
inner.add_child(this.message); inner.add_child(this.message);
this.actor.add_child(this.icon); this.actor.add_child(this.icon);
@ -521,16 +591,27 @@ const PromptDialogHeader = new Lang.Class({
this.icon.icon_name = 'security-high-symbolic'; this.icon.icon_name = 'security-high-symbolic';
}, },
setRemainingPrompts: function(remaining) {
if (!remaining) {
this.waiting.text;
} else {
this.waiting.text = "Remaining: " + remaining;
}
},
}); });
//XXX: InhibitShortcutsDialog
const PromptDialog = new Lang.Class({ const PromptDialog = new Lang.Class({
Name: 'PromptDialog', Name: 'PromptDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
_init: function(invocation, pid_known, sandboxed, tlsguard, cbClose) { _init: function(guid, timestamp, pid_known, sandboxed, tlsguard, cbClose) {
this.cbClose = cbClose; this.cbClose = cbClose;
this.parent({ styleClass: 'fw-prompt-dialog' }); this.parent({ styleClass: 'fw-prompt-dialog' });
this._invocation = invocation; this._guid = guid;
this._timestamp = timestamp;
this._expert = false;
this.header = new PromptDialogHeader(); this.header = new PromptDialogHeader();
this.contentLayout.add_child(this.header.actor); this.contentLayout.add_child(this.header.actor);
@ -552,7 +633,20 @@ const PromptDialog = new Lang.Class({
if (tlsguard) { if (tlsguard) {
this.optionList.addTLSOption(true); this.optionList.addTLSOption(true);
} }
/*
this.buttonToggleTLSGuard = null;
let button = new St.Button({ style_class: "button",
button_mask: St.ButtonMask.ONE,
reactive: true,
can_focus: true,
x_expand: true,
y_expand: true,
label: "Enable TLS Guard" });
button.connect("clicked", Lang.bind(this, this.onToggleTLSGuard));
let actor = new St.BoxLayout({style_class: "fw-toggle-tlsguard"});
actor.add_child(button);
this.contentLayout.add(actor);
*/
this._initialKeyFocusDestroyId = 1; this._initialKeyFocusDestroyId = 1;
this.setButtons([ this.setButtons([
{ label: "Allow", action: Lang.bind(this, this.onAllow) }, { label: "Allow", action: Lang.bind(this, this.onAllow) },
@ -560,6 +654,10 @@ const PromptDialog = new Lang.Class({
]); ]);
}, },
activate: function() {
this.open();
},
_onPromptScopeNext: function() { _onPromptScopeNext: function() {
if (this.details.isOpen) { if (this.details.isOpen) {
this.optionList.scopeNext(); this.optionList.scopeNext();
@ -603,23 +701,22 @@ const PromptDialog = new Lang.Class({
}, },
onAllow: function() { onAllow: function() {
if (this.cbClose !== undefined && this.cbClose !== null) {
this.cbClose();
}
this.close(); this.close();
this.sendReturnValue(true); this.sendReturnValue(true);
}, },
onDeny: function() { onDeny: function() {
if (this.cbClose !== undefined && this.cbClose !== null) {
this.cbClose();
}
this.close(); this.close();
this.sendReturnValue(false); this.sendReturnValue(false);
}, },
/*
onToggleTLSGuard: function(item) {
log("SGFW: Toggle TLS Guard: " + item);
},
*/
sendReturnValue: function(allow) { sendReturnValue: function(allow) {
if (!this._invocation) { if (!this._guid || this.cbClose === undefined || this.cbClose === null) {
return; return;
} }
let verb = "DENY"; let verb = "DENY";
@ -631,18 +728,24 @@ const PromptDialog = new Lang.Class({
verb = "ALLOW"; verb = "ALLOW";
} }
} }
let rule = verb + "|" + this.ruleTarget() + "|" + this.ruleSandbox(); //ALLOW|tcp:textsecure-service-ca.whispersystems.org:4433|-1:-1|PERMANENT
//DENY|subgraph.com:8183|-1:-1|0
let scope = this.optionList.selectedScope(); let privs = ["-1", "-1"];
this._invocation.return_value(GLib.Variant.new('(is)', [scope, rule])); if (this.info.user[1].actor.checked === true) {
this._invocation = null; privs[0] = this._privs.uid;
}
if (this.info.group[1].actor.checked === true) {
privs[1] = this._privs.gid;
}
let rule = verb + "|" + this.ruleTarget() + "|" + privs.join(":") + "|" + this.optionList.scopeToString();
this.cbClose(this._guid, this._timestamp, rule, this.optionList.selectedScope());
}, },
ruleTarget: function() { ruleTarget: function() {
let base = ""; let base = "";
if (this._proto != "tcp") { //if (this._proto != "tcp") {
base = this._proto + ":"; base = this._proto + ":";
} //}
switch(this.optionList.selectedIdx()) { switch(this.optionList.selectedIdx()) {
case 0: case 0:
return base + this._address + ":" + this._port; return base + this._address + ":" + this._port;
@ -669,6 +772,8 @@ const PromptDialog = new Lang.Class({
this._proto = proto; this._proto = proto;
this._sandbox = sandbox; this._sandbox = sandbox;
this._tlsGuard = tlsguard; this._tlsGuard = tlsguard;
this._expert = expert;
this._privs = {"uid": uid, "gid": gid};
let port_str = (proto+"").toUpperCase() + " Port "+ port; let port_str = (proto+"").toUpperCase() + " Port "+ port;
@ -707,7 +812,7 @@ const PromptDialog = new Lang.Class({
this.optionList.setOptionText(0, "Only "+ address + " on "+ port_str); this.optionList.setOptionText(0, "Only "+ address + " on "+ port_str);
} }
if (expert) { if (this._expert) {
if (proto == "icmp") { if (proto == "icmp") {
this.optionList.setOptionText(1, "Only "+ address + " with any ICMP code"); this.optionList.setOptionText(1, "Only "+ address + " with any ICMP code");
} else if (proto == "udp") { } else if (proto == "udp") {
@ -727,6 +832,11 @@ const PromptDialog = new Lang.Class({
} }
this.optionList.buttonGroup._setChecked(this.optionList.scopeToIdx(action)) this.optionList.buttonGroup._setChecked(this.optionList.scopeToIdx(action))
this.info.setDetails(ip, path, pid, uid, gid, user, group, origin, proto, optstring, sandbox); this.info.setDetails(ip, path, pid, uid, gid, user, group, origin, proto, optstring, sandbox, this._expert);
}, },
updateRemainingPrompts: function(count) {
this.header.setRemainingPrompts(count);
},
}); });

@ -1,9 +1,12 @@
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Main = imports.ui.main; const Main = imports.ui.main;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Extension = imports.misc.extensionUtils.getCurrentExtension(); const Extension = imports.misc.extensionUtils.getCurrentExtension();
const Convenience = Extension.imports.convenience; const Convenience = Extension.imports.convenience;
@ -45,10 +48,10 @@ const FirewallSupport = new Lang.Class({
}); });
// $ busctl --user call com.subgraph.FirewallPrompt /com/subgraph/FirewallPrompt com.subgraph.FirewallPrompt TestPrompt
const FirewallPromptInterface = '<node> \ const FirewallPromptInterface = '<node> \
<interface name="com.subgraph.FirewallPrompt"> \ <interface name="com.subgraph.FirewallPrompt"> \
<method name="RequestPrompt"> \ <method name="RequestPromptAsync"> \
<arg type="s" direction="in" name="guid" /> \
<arg type="s" direction="in" name="application" /> \ <arg type="s" direction="in" name="application" /> \
<arg type="s" direction="in" name="icon" /> \ <arg type="s" direction="in" name="icon" /> \
<arg type="s" direction="in" name="path" /> \ <arg type="s" direction="in" name="path" /> \
@ -64,15 +67,17 @@ const FirewallPromptInterface = '<node> \
<arg type="i" direction="in" name="pid" /> \ <arg type="i" direction="in" name="pid" /> \
<arg type="s" direction="in" name="sandbox" /> \ <arg type="s" direction="in" name="sandbox" /> \
<arg type="b" direction="in" name="tlsguard" /> \ <arg type="b" direction="in" name="tlsguard" /> \
<arg type="s" direction="in" name="timestamp" /> \
<arg type="s" direction="in" name="optstring" /> \ <arg type="s" direction="in" name="optstring" /> \
<arg type="b" direction="in" name="expanded" /> \ <arg type="b" direction="in" name="expanded" /> \
<arg type="b" direction="in" name="expert" /> \ <arg type="b" direction="in" name="expert" /> \
<arg type="i" direction="in" name="action" /> \ <arg type="i" direction="in" name="action" /> \
<arg type="i" direction="out" name="scope" /> \ <arg type="b" direction="out" name="result" /> \
<arg type="s" direction="out" name="rule" /> \ </method> \
<method name="RemovePrompt"> \
<arg type="s" direction="in" name="guid" /> \
<arg type="b" direction="out" name="result" /> \
</method> \ </method> \
<method name="ClosePrompt"/> \
<method name="TestPrompt"/> \
</interface> \ </interface> \
</node>'; </node>';
@ -84,8 +89,13 @@ const FirewallPromptHandler = new Lang.Class({
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FirewallPromptInterface, this); this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FirewallPromptInterface, this);
this._dbusImpl.export(Gio.DBus.system, '/com/subgraph/FirewallPrompt'); this._dbusImpl.export(Gio.DBus.system, '/com/subgraph/FirewallPrompt');
Gio.bus_own_name_on_connection(Gio.DBus.system, 'com.subgraph.FirewallPrompt', Gio.BusNameOwnerFlags.REPLACE, null, null); Gio.bus_own_name_on_connection(Gio.DBus.system, 'com.subgraph.FirewallPrompt', Gio.BusNameOwnerFlags.REPLACE, null, null);
this._dialogs = new Array(); this._dialogs = new Object();
this._dialog = null;
this._guids = new Array();
this._current_guid = null;
this._timeoutId = null;
this._initKeybindings(); this._initKeybindings();
this.RequestPendingPrompts();
}, },
destroy: function() { destroy: function() {
@ -93,6 +103,10 @@ const FirewallPromptHandler = new Lang.Class({
this._closeDialogs(); this._closeDialogs();
this._dbusImpl.unexport(); this._dbusImpl.unexport();
this._destroyKeybindings(); this._destroyKeybindings();
if (this._timeoutId !== null) {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = null;
}
}, },
_initKeybindings: function() { _initKeybindings: function() {
@ -116,71 +130,187 @@ const FirewallPromptHandler = new Lang.Class({
}, },
_handleKeybinding: function(a, b, c, d, binding) { _handleKeybinding: function(a, b, c, d, binding) {
if (this._dialogs.length <= 0) { if (this._dialog === null) {
return false; return false;
} }
let fname = binding.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); let fname = binding.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
let fname = "_on"+ fname[0].toUpperCase() + fname.substr(1); let fname = "_on"+ fname[0].toUpperCase() + fname.substr(1);
if (!( fname in this._dialogs[0] )) { if (!( fname in this._dialog )) {
log("SGFW: Invalid key binding (1)... " + fname); log("SGFW: Invalid key binding (1)... " + fname);
return true; return true;
} }
let fn = this._dialogs[0][fname]; let fn = this._dialog[fname];
if (typeof fn !== "function") { if (typeof fn !== "function") {
log("SGFW: Invalid key binding (2)... " + fname + " " + (typeof fn)); log("SGFW: Invalid key binding (2)... " + fname + " " + (typeof fn));
return true; return true;
} }
Lang.bind(this._dialogs[0], fn)(); Lang.bind(this._dialog, fn)();
return true; return true;
}, },
_destroyKeybindings: function() { _destroyKeybindings: function() {
for (var i = 0 , ii = keyBindings.length; i < ii; i++) { for (var i = 0 , ii = this._keyBindings.length; i < ii; i++) {
Main.wm.removeKeybinding(this._keyBindings[i]); Main.wm.removeKeybinding(this._keyBindings[i]);
} }
}, },
_closeDialogs: function() { _closeDialogs: function() {
log("SGFW: Closing all dialogs"); log("SGFW: Closing all dialogs");
if (this._dialogs.length > 0) { if (this._dialog !== null) {
dialog = this._dialogs.shift(); this._dialog.close();
dialog.close(); this._dialog = null;
}
this._dialogs = new Object();
this._guids = new Array();
this._current_guid = null;
},
RequestPendingPrompts: function() {
try {
let params = GLib.Variant.new("(s)", ["*"]);
let result = Gio.DBus.system.call_sync("com.subgraph.Firewall",
"/com/subgraph/Firewall",
"com.subgraph.Firewall",
"GetPendingRequests", params, null,
Gio.DBusCallFlags.NONE, 500, null);
log("SGFW: Get Pending Requests: " + result.deep_unpack());
} catch (err if err.matches(Gio.DBusError, Gio.DBusError.SERVICE_UNKNOWN)) {
return;
} catch (err) {
log("SGFW: Fatal Error Requesting Pending Prompts: "+ err);
} }
}, },
RequestPromptAsync: function(params, invocation) { RequestPromptAsyncAsync: function(params) {
let [app, icon, path, address, port, ip, origin, proto, uid, gid, user, group, pid, sandbox, tlsguard, optstring, expanded, expert, action] = params; try {
let cbfn = function(self) { if (this._dialog == null) {
return function() { return self.onCloseDialog(); } this._dialog = true;
}(this)
let guid = params.shift();
this._dialogs[guid] = params;
this._guids.push(guid);
this._createDialog(guid);
} else {
let guid = params.shift();
this._dialogs[guid] = params;
this._guids.push(guid);
this._updateDialogRemainingPrompts();
}
log("SGFW: Async Prompt Requested " + params);
} catch (err) {
log("SGFW: Error on async prompt request: " + err);
}
},
RemovePromptAsync: function(params, invocation) {
let [guid] = params;
log("SGFW: Async Prompt Remove " + guid + " " + (guid in this._dialogs));
try {
if (guid == this._current_guid) {
this._dialog = null;
this._current_guid = null;
}
if (guid in this._dialogs) {
delete this._dialogs[guid];
for (let ii = (this._guids.length - 1); ii >= 0; ii--) {
if (this._guids[ii] === guid) {
this._guids.splice(ii,1);
break;
}
}
let l = this._dialogs.push(new Dialog.PromptDialog(invocation, (pid >= 0), (sandbox != ""), tlsguard, cbfn)); invocation.return_value(GLib.Variant.new('(b)', [true]));
let dialog = this._dialogs[l-1] } else {
dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, tlsguard, optstring, sandbox, expanded, expert, action); invocation.return_value(GLib.Variant.new('(b)', [false]));
if (this._dialogs.length == 1) { }
dialog.open(); if (this._dialog !== null) {
this._updateDialogRemainingPrompts();
}
} catch (err) {
log("SGFW: Error on async prompt remove: " + err);
}
try {
if (this._timeoutId == null) {
log("SGFW: Waiting to check for next dialog...");
this._timeoutId = Mainloop.timeout_add_seconds(1, Lang.bind(this, this._createNextDialogCallback));
} else {
log("SGFW: Already waiting for next dialog...");
}
} catch (err) {
log("SGFW: Error while setting up next event display timeout: " + err);
} }
}, },
onCloseDialog: function() { AddRuleCallback: function(guid, timestamp, rule, scope) {
log("SGFW: Closed dialog"); log("SGFW: Adding rule for " + guid + " " + timestamp + ": " + rule + " (" + scope + ")");
this._dialogs.shift(); try {
if (this._dialogs.length > 0) { let params = GLib.Variant.new("(usss)", [scope, rule, "*", guid]);
log("SGFW: Opening next dialogs (remaining: " + this._dialogs.length + ")"); let result = Gio.DBus.system.call_sync("com.subgraph.Firewall",
this._dialogs[0].open(); "/com/subgraph/Firewall",
"com.subgraph.Firewall",
"AddRuleAsync", params, null,
Gio.DBusCallFlags.NONE, 500, null);
log("SGFW: Add Rule Async: " + result.deep_unpack());
// XXXX: If false prompt user about failure
} catch (err) {
log("SGFW: Fatal Error: " + err);
} }
}, },
CloseAsync: function(params, invocation) { _createDialog: function(guid) {
log("SGFW: Close Async Requested"); log("SGFW: Creating new prompt for: " + this._dialogs[guid]);
this._closeDialogs(); try {
let [app, icon, path, address, port, ip, origin, proto, uid, gid, user, group, pid, sandbox, tlsguard, timestamp, optstring, expanded, expert, action] = this._dialogs[guid];
this._current_guid = guid;
this._dialog = new Dialog.PromptDialog(guid, timestamp, (pid >= 0), (sandbox != ""), tlsguard, Lang.bind(this, this.AddRuleCallback));
this._dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, tlsguard, optstring, sandbox, expanded, expert, action);
this._updateDialogRemainingPrompts();
this._dialog.activate();
} catch(err) {
log("SGFW: Error while creating dialog: " + err);
}
}, },
_createNextDialogCallback: function() {
log("SGFW: Checking for next dialog...");
try {
if (this._guids.length > 0 && this._current_guid === null) {
log("SGFW: Opening next dialog: " + this._guids[0] + " (remaining: " + this._guids.length + ")");
this._createDialog(this._guids[0]);
}
Mainloop.source_remove(this._timeoutId);
this._timeoutId = null;
} catch (err) {
log("SGFW: Error on creating next dialog callback: " + err);
}
},
_updateDialogRemainingPrompts: function() {
if (this._dialog === null) {
return;
}
try {
let remaining = (this._guids.length - 1);
if (remaining > 0) {
this._dialog.updateRemainingPrompts(remaining);
}
} catch(err) {
log("SGFW: Error while updating remaining dialogs count: " + err);
}
return;
}
/*
TestPrompt: function(params, invocation) { TestPrompt: function(params, invocation) {
log("SGFW: Test Prompt Requested"); log("SGFW: Test Prompt Requested");
this.RequestPromptAsync(["Firefox", "firefox", "/usr/bin/firefox-esr", "242.12.111.18", "443", "linux", "2342", "TCP", true, true], nil); this.RequestPromptAsync(["Firefox", "firefox", "/usr/bin/firefox-esr", "242.12.111.18", "443", "linux", "2342", "TCP", true, true], nil);
} }
*/
}); });

@ -50,6 +50,11 @@
font-weight: bold; font-weight: bold;
} }
.fw-prompt-waiting {
font-size: 10pt;
text-align: right;
}
.fw-prompt-icon { .fw-prompt-icon {
padding: 10px; padding: 10px;
} }
@ -78,3 +83,33 @@
.fw-details-left { .fw-details-left {
padding-right: 10px; padding-right: 10px;
} }
.fw-toggle-tlsguard {
margin-top: 1.75em;
}
.fw-details-section .check-box {
margin: 0 !important;
padding: 0 !important;
vertical-align: middle;
}
.fw-details-section .check-box StBoxLayout {
margin: 0 !important;
padding: 0 !important;
spacing: 0.3em;
vertical-align: middle;
}
.fw-details-section .check-box StBin {
margin: 0 !important;
padding: 0 !important;
vertical-align: middle;
}
.fw-details-section .check-box StLabel {
margin: 0 !important;
padding: 0 !important;
padding-top: 2px !important;
vertical-align: middle;
}

@ -4,6 +4,12 @@ import (
"github.com/subgraph/fw-daemon/sgfw" "github.com/subgraph/fw-daemon/sgfw"
) )
func init() {
sgfw.InitVirtual()
sgfw.InitIPC()
sgfw.InitPrompt()
}
func main() { func main() {
sgfw.Main() sgfw.Main()
} }

@ -145,5 +145,10 @@ type DbusRule struct {
Verb uint16 Verb uint16
Target string Target string
Mode uint16 Mode uint16
IsSocks bool
Sandbox string Sandbox string
UID int32
GID int32
Username string
Group string
} }

@ -2,7 +2,6 @@ package sgfw
import ( import (
"errors" "errors"
"fmt"
"net" "net"
"path" "path"
"strconv" "strconv"
@ -27,7 +26,7 @@ const introspectXML = `
</method> </method>
<method name="ListRules"> <method name="ListRules">
<arg name="rules" direction="out" type="a(ussus)" /> <arg name="rules" direction="out" type="a(usssusssqsqbsuuss)" />
</method> </method>
<method name="DeleteRule"> <method name="DeleteRule">
@ -46,6 +45,28 @@ const introspectXML = `
<arg name="key" direction="in" type="s" /> <arg name="key" direction="in" type="s" />
<arg name="val" direction="in" type="v" /> <arg name="val" direction="in" type="v" />
</method> </method>
<method name="GetPendingRequests">
<arg name="policy" direction="in" type="s" />
<arg name="result" direction="out" type="b" />
</method>
<method name="AddRuleAsync">
<arg name="scope" direction="in" type="u" />
<arg name="rule" direction="in" type="s" />
<arg name="policy" direction="in" type="s" />
<arg name="guid" direction="in" type="s" />
<arg name="result" direction="out" type="b" />
</method>
<method name="AddNewRule">
<arg name="rule" direction="in" type="usssusssqsqsuuss" />
<arg name="result" direction="out" type="b" />
</method>
<signal name="Refresh">
<arg name="refresh_event" type="s" />
</signal>
</interface>` + </interface>` +
introspect.IntrospectDataString + introspect.IntrospectDataString +
`</node>` `</node>`
@ -58,15 +79,6 @@ type dbusObjectP struct {
dbus.BusObject dbus.BusObject
} }
func newDbusObjectPrompt() (*dbusObjectP, error) {
conn, err := dbus.SystemBus()
if err != nil {
return nil, err
}
return &dbusObjectP{conn.Object("com.subgraph.fwprompt.EventNotifier", "/com/subgraph/fwprompt/EventNotifier")}, nil
}
func newDbusRedactedLogger() (*dbusObjectP, error) { func newDbusRedactedLogger() (*dbusObjectP, error) {
conn, err := dbus.SystemBus() conn, err := dbus.SystemBus()
if err != nil { if err != nil {
@ -103,7 +115,7 @@ func DbusProcDeathCB(pid int, param interface{}) {
} }
if updated { if updated {
dbusp.alertRule("Firewall removed on process death") ds.fw.dbus.emitRefresh("removed")
} }
} }
@ -130,7 +142,7 @@ func newDbusServer() (*dbusServer, error) {
} }
ds.conn = conn ds.conn = conn
ds.prompter = newPrompter(conn) ds.prompter = newPrompter(ds)
pcoroner.AddCallback(DbusProcDeathCB, ds) pcoroner.AddCallback(DbusProcDeathCB, ds)
return ds, nil return ds, nil
} }
@ -168,7 +180,7 @@ func createDbusRule(r *Rule) DbusRule {
pstr += ":" + strconv.Itoa(r.gid) pstr += ":" + strconv.Itoa(r.gid)
} }
return DbusRule{ return DbusRule {
ID: uint32(r.id), ID: uint32(r.id),
Net: netstr, Net: netstr,
Origin: ostr, Origin: ostr,
@ -180,7 +192,12 @@ func createDbusRule(r *Rule) DbusRule {
Verb: uint16(r.rtype), Verb: uint16(r.rtype),
Target: r.AddrString(false), Target: r.AddrString(false),
Mode: uint16(r.mode), Mode: uint16(r.mode),
Sandbox: r.sandbox, IsSocks: false,//r.is_socks,
Sandbox: r.policy.sandbox,
UID: int32(r.uid),
GID: int32(r.gid),
Username: r.uname,
Group: r.gname,
} }
} }
@ -207,7 +224,7 @@ func (ds *dbusServer) DeleteRule(id uint32) *dbus.Error {
if r != nil { if r != nil {
r.policy.removeRule(r) r.policy.removeRule(r)
} }
if r.mode != RULE_MODE_SESSION { if r.mode != RULE_MODE_SESSION && r.mode != RULE_MODE_PROCESS {
ds.fw.saveRules() ds.fw.saveRules()
} }
return nil return nil
@ -276,14 +293,14 @@ func (ds *dbusServer) GetPendingRequests(policy string) (bool, *dbus.Error) {
} }
func (ds *dbusServer) AddRuleAsync(scope uint32, rule, policy, guid string) (bool, *dbus.Error) { func (ds *dbusServer) AddRuleAsync(scope uint32, rule, policy, guid string) (bool, *dbus.Error) {
log.Warningf("AddRuleAsync %v, %v / %v / %v\n", scope, rule, policy, guid) log.Debug("AddRuleAsync %v, %v / %v / %v\n", scope, rule, policy, guid)
ds.fw.lock.Lock() ds.fw.lock.Lock()
defer ds.fw.lock.Unlock() defer ds.fw.lock.Unlock()
prule := PendingRule{rule: rule, scope: int(scope), policy: policy, guid: guid} prule := PendingRule{rule: rule, scope: int(scope), policy: policy, guid: guid}
for pname := range ds.fw.policyMap { for pname := range ds.fw.policyMap {
log.Debug("+++ Adding prule to policy") log.Debugf("+++ Adding prule to policy: %s >>> %+b", pname, ds.fw.policyMap[pname].promptInProgress)
ds.fw.policyMap[pname].rulesPending = append(ds.fw.policyMap[pname].rulesPending, prule) ds.fw.policyMap[pname].rulesPending = append(ds.fw.policyMap[pname].rulesPending, prule)
} }
@ -335,6 +352,88 @@ func (ds *dbusServer) AddTestVPC(proto string, srcip string, sport uint16, dstip
return true, nil return true, nil
} }
func (ds *dbusServer) AddNewRule(rule DbusRule) (bool, *dbus.Error) {
log.Debugf("AddNewRule %+v\n", rule)
var pn *Policy
if rule.Sandbox != "" {
pn = ds.fw.PolicyForPathAndSandbox(rule.Path, rule.Sandbox)
} else {
pn = ds.fw.PolicyForPath(rule.Path)
}
if RuleMode(rule.Mode) == RULE_MODE_SYSTEM {
log.Warningf("Cannot modify system rule: %+v", rule)
return false,nil
}
if rule.ID != 0 {
if RuleMode(rule.Mode) != RULE_MODE_PROCESS && RuleMode(rule.Mode) != RULE_MODE_SESSION {
log.Warningf("Saving a session/process rule as new without an ID?")
return false,nil
}
ds.fw.lock.Lock()
rr := ds.fw.rulesByID[uint(rule.ID)]
ds.fw.lock.Unlock()
if rr == nil {
log.Noticef("Saving a session/process rule as new without a valid ID?")
} else {
rr.policy.lock.Lock()
rr.policy.removeRule(rr)
rr.policy.lock.Unlock()
}
rule.ID = 0
rule.Mode = uint16(RULE_MODE_PERMANENT)
}
/*
pn.lock.Lock()
defer pn.lock.Unlock()
if RuleMode(rule.Mode) == RULE_MODE_PROCESS || RuleMode(rule.Mode) == RULE_MODE_SESSION {
if rule.ID == 0 {
log.Warningf("Saving a session/process rule as new without an ID?")
return false,nil
}
ds.fw.lock.Lock()
rr := ds.fw.rulesByID[uint(rule.ID)]
ds.fw.lock.Unlock()
if rr == nil {
log.Warningf("Saving a session/process rule as new without a valid ID?")
return false,nil
}
pn.removeRule(rr)
rule.Mode = uint16(RULE_MODE_PERMANENT)
}
*/
r := new(Rule)
r.addr = noAddress
if !r.parseTarget(rule.Target) {
log.Warningf("Unable to parse target: %s", rule.Target)
return false, nil
}
if RuleAction(rule.Verb) == RULE_ACTION_ALLOW || RuleAction(rule.Verb) == RULE_ACTION_ALLOW_TLSONLY || RuleAction(rule.Verb) == RULE_ACTION_DENY {
r.rtype = RuleAction(rule.Verb)
}
r.hostname = r.hostname
r.addr = r.addr
r.proto = rule.Proto
r.port = r.port
r.uid = int(rule.UID)
r.gid = int(rule.GID)
r.mode = RuleMode(rule.Mode)
r.policy = pn
ds.fw.addRule(r)
pn.lock.Lock()
pn.rules = append(pn.rules, r)
pn.lock.Unlock()
if r.mode != RULE_MODE_SESSION && r.mode != RULE_MODE_PROCESS {
ds.fw.saveRules()
}
ds.fw.dbus.emitRefresh("rules")
return true, nil
}
func (ds *dbusServer) UpdateRule(rule DbusRule) *dbus.Error { func (ds *dbusServer) UpdateRule(rule DbusRule) *dbus.Error {
log.Debugf("UpdateRule %v", rule) log.Debugf("UpdateRule %v", rule)
ds.fw.lock.Lock() ds.fw.lock.Lock()
@ -352,20 +451,25 @@ func (ds *dbusServer) UpdateRule(rule DbusRule) *dbus.Error {
return nil return nil
} }
r.policy.lock.Lock() r.policy.lock.Lock()
if RuleAction(rule.Verb) == RULE_ACTION_ALLOW || RuleAction(rule.Verb) == RULE_ACTION_DENY { if RuleAction(rule.Verb) == RULE_ACTION_ALLOW || RuleAction(rule.Verb) == RULE_ACTION_ALLOW_TLSONLY || RuleAction(rule.Verb) == RULE_ACTION_DENY {
r.rtype = RuleAction(rule.Verb) r.rtype = RuleAction(rule.Verb)
} }
r.hostname = tmp.hostname r.hostname = tmp.hostname
r.proto = tmp.proto r.proto = rule.Proto
r.pid = tmp.pid //r.pid = tmp.pid
r.addr = tmp.addr r.addr = tmp.addr
r.port = tmp.port r.port = tmp.port
r.uid = int(rule.UID)
r.gid = int(rule.GID)
r.mode = RuleMode(rule.Mode) r.mode = RuleMode(rule.Mode)
r.sandbox = rule.Sandbox
r.policy.lock.Unlock() r.policy.lock.Unlock()
if r.mode != RULE_MODE_SESSION { if r.mode != RULE_MODE_SESSION && r.mode != RULE_MODE_PROCESS {
ds.fw.saveRules() ds.fw.saveRules()
} }
ds.fw.dbus.emitRefresh("rules")
} else {
log.Warning("Failed to update rule, rule id `%d` missing.", rule.ID)
} }
return nil return nil
} }
@ -401,11 +505,12 @@ func (ds *dbusServer) SetConfig(key string, val dbus.Variant) *dbus.Error {
FirewallConfig.DefaultActionID = FilterScope(l) FirewallConfig.DefaultActionID = FilterScope(l)
} }
writeConfig() writeConfig()
ds.emitRefresh("config")
return nil return nil
} }
func (ob *dbusObjectP) alertRule(data string) { func (ds *dbusServer) emitRefresh(data string) {
ob.Call("com.subgraph.fwprompt.EventNotifier.Alert", 0, data) ds.conn.Emit("/com/subgraph/Firewall", "com.subgraph.Firewall.Refresh", data)
} }
func (ob *dbusObjectP) logRedacted(level string, logline string) bool { func (ob *dbusObjectP) logRedacted(level string, logline string) bool {
@ -420,7 +525,7 @@ func (ob *dbusObjectP) logRedacted(level string, logline string) bool {
err := call.Store(&dres) err := call.Store(&dres)
if err != nil { if err != nil {
fmt.Println("Error sending redacted log message to sublogmon:", err) log.Warningf("Error sending redacted log message to sublogmon:", err)
return false return false
} }

@ -332,7 +332,7 @@ const OzSocketName = "@oz-control"
var bSockName = OzSocketName var bSockName = OzSocketName
func init() { func InitIPC() {
bSockName = os.Getenv("SOCKET_NAME") bSockName = os.Getenv("SOCKET_NAME")
if bSockName != "" { if bSockName != "" {

@ -16,10 +16,11 @@ import (
var gPrompter *prompter = nil var gPrompter *prompter = nil
func newPrompter(conn *dbus.Conn) *prompter { func newPrompter(dbus *dbusServer) *prompter {
p := new(prompter) p := new(prompter)
p.dbus = dbus
p.cond = sync.NewCond(&p.lock) p.cond = sync.NewCond(&p.lock)
p.dbusObj = conn.Object("com.subgraph.FirewallPrompt", "/com/subgraph/FirewallPrompt") p.dbusObj = p.dbus.conn.Object("com.subgraph.FirewallPrompt", "/com/subgraph/FirewallPrompt")
p.policyMap = make(map[string]*Policy) p.policyMap = make(map[string]*Policy)
if gPrompter != nil { if gPrompter != nil {
@ -32,6 +33,7 @@ func newPrompter(conn *dbus.Conn) *prompter {
} }
type prompter struct { type prompter struct {
dbus *dbusServer
dbusObj dbus.BusObject dbusObj dbus.BusObject
lock sync.Mutex lock sync.Mutex
cond *sync.Cond cond *sync.Cond
@ -265,7 +267,7 @@ func monitorPromptFDLoop() {
} }
func init() { func InitPrompt() {
go monitorPromptFDLoop() go monitorPromptFDLoop()
} }
@ -388,7 +390,7 @@ func (p *prompter) processConnection(pc pendingConnection) {
policy.fw.saveRules() policy.fw.saveRules()
} }
//log.Warningf("Prompt returning rule: %v", tempRule) //log.Warningf("Prompt returning rule: %v", tempRule)
dbusp.alertRule("sgfw prompt added new rule") p.dbus.emitRefresh("rules")
} }
func (p *prompter) nextConnection() (pendingConnection, bool) { func (p *prompter) nextConnection() (pendingConnection, bool) {
@ -498,7 +500,7 @@ func (p *prompter) nextConnection() (pendingConnection, bool) {
policy.fw.saveRules() policy.fw.saveRules()
} }
//log.Warningf("Prompt returning rule: %v", tempRule) //log.Warningf("Prompt returning rule: %v", tempRule)
dbusp.alertRule("sgfw prompt added new rule") p.dbus.emitRefresh("rules")
} }
} }

@ -267,18 +267,13 @@ func (r *Rule) parse(s string) bool {
return false return false
} }
if !r.parseSandbox(parts[4]) {
log.Warning("invalid sandbox ", parts[4], "in line ", s)
return false
}
// fmt.Printf("uid = %v, gid = %v, user = %v, group = %v, hostname = %v, sandbox = %v\n", r.uid, r.gid, r.uname, r.gname, r.hostname, r.sandbox) // fmt.Printf("uid = %v, gid = %v, user = %v, group = %v, hostname = %v, sandbox = %v\n", r.uid, r.gid, r.uname, r.gname, r.hostname, r.sandbox)
if len(parts) == 6 && len(strings.TrimSpace(parts[5])) > 0 { if len(parts) == 5 && len(strings.TrimSpace(parts[4])) > 0 {
r.saddr = net.ParseIP(parts[5]) r.saddr = net.ParseIP(parts[4])
if r.saddr == nil { if r.saddr == nil {
log.Notice("Error: invalid source IP ", parts[5], " in line ", s) log.Notice("Error: invalid source IP ", parts[4], " in line ", s)
return false return false
} }

@ -21,7 +21,6 @@ import (
"github.com/subgraph/go-procsnitch" "github.com/subgraph/go-procsnitch"
) )
var dbusp *dbusObjectP = nil
var dbLogger *dbusObjectP = nil var dbLogger *dbusObjectP = nil
type Firewall struct { type Firewall struct {
@ -262,17 +261,12 @@ func Main() {
log.Notice("Did not find SOCKS5 configuration file at", scfile, "; ignoring subsystem...") log.Notice("Did not find SOCKS5 configuration file at", scfile, "; ignoring subsystem...")
} }
dbusp, err = newDbusObjectPrompt()
if err != nil {
panic(fmt.Sprintf("Failed to connect to DBus system bus for SGFW prompt events: %v", err))
}
dbLogger, err = newDbusRedactedLogger() dbLogger, err = newDbusRedactedLogger()
if err != nil { if err != nil {
panic(fmt.Sprintf("Failed to connect to DBus system bus for redacted logger: %v", err)) panic(fmt.Sprintf("Failed to connect to DBus system bus for redacted logger: %v", err))
} }
dbusp.alertRule("fw-daemon initialization") fw.dbus.emitRefresh("init")
go OzReceiver(fw) go OzReceiver(fw)

@ -31,7 +31,7 @@ var tdb *dbusObjectP
var tdbMutex = &sync.Mutex{} var tdbMutex = &sync.Mutex{}
var tdbInit = false var tdbInit = false
func init() { func InitVirtual() {
fmt.Println("Initializing virtual packet test subsystem...") fmt.Println("Initializing virtual packet test subsystem...")
conn, err := dbus.SystemBus() conn, err := dbus.SystemBus()

@ -0,0 +1,12 @@
[Desktop Entry]
Name=Subgraph Firewall Settings
Comment=Launch the Subgraph Firewall Settings
Keywords=Network;Security;Settings;Subgraph;Firewall;fwdaemon;
TryExec=fw-settings
Exec=fw-settings
Terminal=false
Type=Application
Icon=security-medium
Categories=Security;Settings;
X-Desktop-File-Install-Version=0.23
DBusActivatable=true

@ -1,4 +1,5 @@
[D-BUS Service] [D-BUS Service]
Name=com.subgraph.FirewallPrompt Name=com.subgraph.FirewallPrompt
Exec=/usr/bin/fw-prompt -display :0 Exec=/usr/bin/fw-settings --prompt-only --display :0
User=user User=user
#SystemdService=fw-settings.service

@ -0,0 +1,31 @@
language: go
go:
- 1.4
- 1.5
- 1.6
- tip
env:
- GOARCH=amd64
sudo: required
dist: trusty
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq -y gtk+3.0 libgtk-3-dev
- sudo apt-get install -qq -y xvfb
- "export DISPLAY=:99.0"
- sudo /usr/bin/Xvfb $DISPLAY 2>1 > /dev/null &
- "export GTK_VERSION=$(pkg-config --modversion gtk+-3.0 | tr . _| cut -d '_' -f 1-2)"
- "export Glib_VERSION=$(pkg-config --modversion glib-2.0)"
- "export Cairo_VERSION=$(pkg-config --modversion cairo)"
- "export Pango_VERSION=$(pkg-config --modversion pango)"
- echo "GTK version ${GTK_VERSION} (Glib ${Glib_VERSION}, Cairo ${Cairo_VERSION}, Pango ${Pango_VERSION})"
install:
- go get -t -tags "gtk_${GTK_VERSION}" github.com/gotk3/gotk3/...
script:
- go test -tags "gtk_${GTK_VERSION}" ./...

@ -124,4 +124,3 @@ func (v *Context) TextExtents(utf8 string) TextExtents {
// TODO: cairo_text_cluster_allocate // TODO: cairo_text_cluster_allocate
// TODO: cairo_text_cluster_free // TODO: cairo_text_cluster_free

@ -267,6 +267,26 @@ const (
WINDOW_STATE_TILED WindowState = C.GDK_WINDOW_STATE_TILED WINDOW_STATE_TILED WindowState = C.GDK_WINDOW_STATE_TILED
) )
// WindowTypeHint is a representation of GDK's GdkWindowTypeHint
type WindowTypeHint int
const (
WINDOW_TYPE_HINT_NORMAL WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_NORMAL
WINDOW_TYPE_HINT_DIALOG WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_DIALOG
WINDOW_TYPE_HINT_MENU WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_MENU
WINDOW_TYPE_HINT_TOOLBAR WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_TOOLBAR
WINDOW_TYPE_HINT_SPLASHSCREEN WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_SPLASHSCREEN
WINDOW_TYPE_HINT_UTILITY WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_UTILITY
WINDOW_TYPE_HINT_DOCK WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_DOCK
WINDOW_TYPE_HINT_DESKTOP WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_DESKTOP
WINDOW_TYPE_HINT_DROPDOWN_MENU WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU
WINDOW_TYPE_HINT_POPUP_MENU WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_POPUP_MENU
WINDOW_TYPE_HINT_TOOLTIP WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_TOOLTIP
WINDOW_TYPE_HINT_NOTIFICATION WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_NOTIFICATION
WINDOW_TYPE_HINT_COMBO WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_COMBO
WINDOW_TYPE_HINT_DND WindowTypeHint = C.GDK_WINDOW_TYPE_HINT_DND
)
// CURRENT_TIME is a representation of GDK_CURRENT_TIME // CURRENT_TIME is a representation of GDK_CURRENT_TIME
const CURRENT_TIME = C.GDK_CURRENT_TIME const CURRENT_TIME = C.GDK_CURRENT_TIME
@ -385,10 +405,7 @@ func CursorNewFromName(display *Display, name string) (*Cursor, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))} return &Cursor{glib.Take(unsafe.Pointer(c))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return &Cursor{obj}, nil
} }
// native returns a pointer to the underlying GdkCursor. // native returns a pointer to the underlying GdkCursor.
@ -446,10 +463,8 @@ func (v *DeviceManager) GetDisplay() (*Display, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
obj.Ref() return &Display{glib.Take(unsafe.Pointer(c))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return &Display{obj}, nil
} }
/* /*
@ -497,11 +512,8 @@ func DisplayOpen(displayName string) (*Display, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
d := &Display{obj} return &Display{glib.Take(unsafe.Pointer(c))}, nil
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return d, nil
} }
// DisplayGetDefault() is a wrapper around gdk_display_get_default(). // DisplayGetDefault() is a wrapper around gdk_display_get_default().
@ -510,11 +522,8 @@ func DisplayGetDefault() (*Display, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
d := &Display{obj} return &Display{glib.Take(unsafe.Pointer(c))}, nil
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return d, nil
} }
// GetName() is a wrapper around gdk_display_get_name(). // GetName() is a wrapper around gdk_display_get_name().
@ -532,11 +541,8 @@ func (v *Display) GetDefaultScreen() (*Screen, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
s := &Screen{obj} return &Screen{glib.Take(unsafe.Pointer(c))}, nil
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return s, nil
} }
// DeviceIsGrabbed() is a wrapper around gdk_display_device_is_grabbed(). // DeviceIsGrabbed() is a wrapper around gdk_display_device_is_grabbed().
@ -577,6 +583,8 @@ func (v *Display) GetEvent() (*Event, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
//The finalizer is not on the glib.Object but on the event.
e := &Event{c} e := &Event{c}
runtime.SetFinalizer(e, (*Event).free) runtime.SetFinalizer(e, (*Event).free)
return e, nil return e, nil
@ -588,6 +596,8 @@ func (v *Display) PeekEvent() (*Event, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
//The finalizer is not on the glib.Object but on the event.
e := &Event{c} e := &Event{c}
runtime.SetFinalizer(e, (*Event).free) runtime.SetFinalizer(e, (*Event).free)
return e, nil return e, nil
@ -645,11 +655,8 @@ func (v *Display) GetDefaultGroup() (*Window, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
w := &Window{obj} return &Window{glib.Take(unsafe.Pointer(c))}, nil
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return w, nil
} }
// SupportsSelectionNotification() is a wrapper around // SupportsSelectionNotification() is a wrapper around
@ -676,6 +683,7 @@ func (v *Display) SupportsClipboardPersistence() bool {
// TODO(jrick) // TODO(jrick)
func (v *Display) StoreClipboard(clipboardWindow *Window, time uint32, targets ...Atom) { func (v *Display) StoreClipboard(clipboardWindow *Window, time uint32, targets ...Atom) {
panic("Not implemented")
} }
// SupportsShapes() is a wrapper around gdk_display_supports_shapes(). // SupportsShapes() is a wrapper around gdk_display_supports_shapes().
@ -692,6 +700,7 @@ func (v *Display) SupportsInputShapes() bool {
// TODO(jrick) glib.AppLaunchContext GdkAppLaunchContext // TODO(jrick) glib.AppLaunchContext GdkAppLaunchContext
func (v *Display) GetAppLaunchContext() { func (v *Display) GetAppLaunchContext() {
panic("Not implemented")
} }
// NotifyStartupComplete() is a wrapper around gdk_display_notify_startup_complete(). // NotifyStartupComplete() is a wrapper around gdk_display_notify_startup_complete().
@ -1257,6 +1266,7 @@ func (v *Pixbuf) GetPixels() (channels []byte) {
sliceHeader.Data = uintptr(unsafe.Pointer(c)) sliceHeader.Data = uintptr(unsafe.Pointer(c))
sliceHeader.Len = int(length) sliceHeader.Len = int(length)
sliceHeader.Cap = int(length) sliceHeader.Cap = int(length)
// To make sure the slice doesn't outlive the Pixbuf, add a reference // To make sure the slice doesn't outlive the Pixbuf, add a reference
v.Ref() v.Ref()
runtime.SetFinalizer(&channels, func(_ *[]byte) { runtime.SetFinalizer(&channels, func(_ *[]byte) {
@ -1308,10 +1318,8 @@ func PixbufNew(colorspace Colorspace, hasAlpha bool, bitsPerSample, width, heigh
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj} return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
// PixbufCopy is a wrapper around gdk_pixbuf_copy(). // PixbufCopy is a wrapper around gdk_pixbuf_copy().
@ -1320,51 +1328,49 @@ func PixbufCopy(v *Pixbuf) (*Pixbuf, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj} return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
// PixbufNewFromFile is a wrapper around gdk_pixbuf_new_from_file(). // PixbufNewFromFile is a wrapper around gdk_pixbuf_new_from_file().
func PixbufNewFromFile(filename string) (*Pixbuf, error) { func PixbufNewFromFile(filename string) (*Pixbuf, error) {
cstr := C.CString(filename) cstr := C.CString(filename)
defer C.free(unsafe.Pointer(cstr)) defer C.free(unsafe.Pointer(cstr))
var err *C.GError var err *C.GError
res := C.gdk_pixbuf_new_from_file((*C.char)(cstr), &err) res := C.gdk_pixbuf_new_from_file((*C.char)(cstr), &err)
if res == nil { if res == nil {
defer C.g_error_free(err) defer C.g_error_free(err)
return nil, errors.New(C.GoString((*C.char)(err.message))) return nil, errors.New(C.GoString((*C.char)(err.message)))
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(res))}
p := &Pixbuf{obj} return &Pixbuf{glib.Take(unsafe.Pointer(res))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
// PixbufNewFromFileAtSize is a wrapper around gdk_pixbuf_new_from_file_at_size(). // PixbufNewFromFileAtSize is a wrapper around gdk_pixbuf_new_from_file_at_size().
func PixbufNewFromFileAtSize(filename string, width, height int) (*Pixbuf, error) { func PixbufNewFromFileAtSize(filename string, width, height int) (*Pixbuf, error) {
cstr := C.CString(filename) cstr := C.CString(filename)
defer C.free(unsafe.Pointer(cstr)) defer C.free(unsafe.Pointer(cstr))
var err *C.GError = nil var err *C.GError = nil
res := C.gdk_pixbuf_new_from_file_at_size(cstr, C.int(width), C.int(height), &err) res := C.gdk_pixbuf_new_from_file_at_size(cstr, C.int(width), C.int(height), &err)
if err != nil { if err != nil {
defer C.g_error_free(err) defer C.g_error_free(err)
return nil, errors.New(C.GoString((*C.char)(err.message))) return nil, errors.New(C.GoString((*C.char)(err.message)))
} }
if res == nil { if res == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(res))}
p := &Pixbuf{obj} return &Pixbuf{glib.Take(unsafe.Pointer(res))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
// PixbufNewFromFileAtScale is a wrapper around gdk_pixbuf_new_from_file_at_scale(). // PixbufNewFromFileAtScale is a wrapper around gdk_pixbuf_new_from_file_at_scale().
func PixbufNewFromFileAtScale(filename string, width, height int, preserveAspectRatio bool) (*Pixbuf, error) { func PixbufNewFromFileAtScale(filename string, width, height int, preserveAspectRatio bool) (*Pixbuf, error) {
cstr := C.CString(filename) cstr := C.CString(filename)
defer C.free(unsafe.Pointer(cstr)) defer C.free(unsafe.Pointer(cstr))
var err *C.GError = nil var err *C.GError = nil
res := C.gdk_pixbuf_new_from_file_at_scale(cstr, C.int(width), C.int(height), res := C.gdk_pixbuf_new_from_file_at_scale(cstr, C.int(width), C.int(height),
gbool(preserveAspectRatio), &err) gbool(preserveAspectRatio), &err)
@ -1372,13 +1378,12 @@ func PixbufNewFromFileAtScale(filename string, width, height int, preserveAspect
defer C.g_error_free(err) defer C.g_error_free(err)
return nil, errors.New(C.GoString((*C.char)(err.message))) return nil, errors.New(C.GoString((*C.char)(err.message)))
} }
if res == nil { if res == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(res))}
p := &Pixbuf{obj} return &Pixbuf{glib.Take(unsafe.Pointer(res))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
// ScaleSimple is a wrapper around gdk_pixbuf_scale_simple(). // ScaleSimple is a wrapper around gdk_pixbuf_scale_simple().
@ -1388,10 +1393,8 @@ func (v *Pixbuf) ScaleSimple(destWidth, destHeight int, interpType InterpType) (
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj} return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
// RotateSimple is a wrapper around gdk_pixbuf_rotate_simple(). // RotateSimple is a wrapper around gdk_pixbuf_rotate_simple().
@ -1400,10 +1403,8 @@ func (v *Pixbuf) RotateSimple(angle PixbufRotation) (*Pixbuf, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj} return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
// ApplyEmbeddedOrientation is a wrapper around gdk_pixbuf_apply_embedded_orientation(). // ApplyEmbeddedOrientation is a wrapper around gdk_pixbuf_apply_embedded_orientation().
@ -1412,10 +1413,8 @@ func (v *Pixbuf) ApplyEmbeddedOrientation() (*Pixbuf, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj} return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
// Flip is a wrapper around gdk_pixbuf_flip(). // Flip is a wrapper around gdk_pixbuf_flip().
@ -1424,10 +1423,8 @@ func (v *Pixbuf) Flip(horizontal bool) (*Pixbuf, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj} return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
// SaveJPEG is a wrapper around gdk_pixbuf_save(). // SaveJPEG is a wrapper around gdk_pixbuf_save().
@ -1444,6 +1441,7 @@ func (v *Pixbuf) SaveJPEG(path string, quality int) error {
defer C.g_error_free(err) defer C.g_error_free(err)
return errors.New(C.GoString((*C.char)(err.message))) return errors.New(C.GoString((*C.char)(err.message)))
} }
return nil return nil
} }
@ -1501,10 +1499,7 @@ func PixbufLoaderNew() (*PixbufLoader, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
//TODO this should be some wrap object p := &PixbufLoader{glib.Take(unsafe.Pointer(c))}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &PixbufLoader{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil return p, nil
} }
@ -1525,11 +1520,7 @@ func PixbufLoaderNewWithType(t string) (*PixbufLoader, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
//TODO this should be some wrap object return &PixbufLoader{glib.Take(unsafe.Pointer(c))}, nil
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &PixbufLoader{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
// Write() is a wrapper around gdk_pixbuf_loader_write(). The // Write() is a wrapper around gdk_pixbuf_loader_write(). The
@ -1582,11 +1573,8 @@ func (v *PixbufLoader) GetPixbuf() (*Pixbuf, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj} return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
} }
type RGBA struct { type RGBA struct {

@ -32,10 +32,8 @@ func (v *DeviceManager) GetClientPointer() (*Device, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
obj.Ref() return &Device{glib.Take(unsafe.Pointer(c))}, nil
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return &Device{obj}, nil
} }
// ListDevices() is a wrapper around gdk_device_manager_list_devices(). // ListDevices() is a wrapper around gdk_device_manager_list_devices().
@ -44,6 +42,8 @@ func (v *DeviceManager) ListDevices(tp DeviceType) *glib.List {
if clist == nil { if clist == nil {
return nil return nil
} }
//TODO: WrapList should set the finalizer
glist := glib.WrapList(uintptr(unsafe.Pointer(clist))) glist := glib.WrapList(uintptr(unsafe.Pointer(clist)))
glist.DataWrapper(func(ptr unsafe.Pointer) interface{} { glist.DataWrapper(func(ptr unsafe.Pointer) interface{} {
return &Device{&glib.Object{glib.ToGObject(ptr)}} return &Device{&glib.Object{glib.ToGObject(ptr)}}
@ -65,11 +65,8 @@ func (v *Display) GetDeviceManager() (*DeviceManager, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
d := &DeviceManager{obj} return &DeviceManager{glib.Take(unsafe.Pointer(c))}, nil
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return d, nil
} }
// GetScreen() is a wrapper around gdk_display_get_screen(). // GetScreen() is a wrapper around gdk_display_get_screen().
@ -78,9 +75,6 @@ func (v *Display) GetScreen(screenNum int) (*Screen, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
s := &Screen{obj} return &Screen{glib.Take(unsafe.Pointer(c))}, nil
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return s, nil
} }

@ -5,7 +5,6 @@ package gdk
// #include "gdk.go.h" // #include "gdk.go.h"
import "C" import "C"
import ( import (
"runtime"
"unsafe" "unsafe"
"github.com/gotk3/gotk3/glib" "github.com/gotk3/gotk3/glib"
@ -54,11 +53,8 @@ func (v *Screen) GetRGBAVisual() (*Visual, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
visual := &Visual{obj} return &Visual{glib.Take(unsafe.Pointer(c))}, nil
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return visual, nil
} }
// GetSystemVisual is a wrapper around gdk_screen_get_system_visual(). // GetSystemVisual is a wrapper around gdk_screen_get_system_visual().
@ -67,11 +63,8 @@ func (v *Screen) GetSystemVisual() (*Visual, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
visual := &Visual{obj} return &Visual{glib.Take(unsafe.Pointer(c))}, nil
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return visual, nil
} }
// ScreenGetDefault is a wrapper aroud gdk_screen_get_default(). // ScreenGetDefault is a wrapper aroud gdk_screen_get_default().

@ -0,0 +1,210 @@
package glib
// #cgo pkg-config: glib-2.0 gobject-2.0
// #include <gio/gio.h>
// #include <glib.h>
// #include <glib-object.h>
// #include "glib.go.h"
import "C"
import "unsafe"
/*
type ActionType int
const (
ACTION_TYPE_ ActionType = C.GTK_
)
*/
// ActionGroup is a representation of GActionGroup.
type ActionGroup struct {
*Object
}
// native() returns a pointer to the underlying GActionGroup.
func (v *ActionGroup) native() *C.GActionGroup {
if v == nil || v.GObject == nil {
return nil
}
return C.toGActionGroup(unsafe.Pointer(v.GObject))
}
func (v *ActionGroup) Native() uintptr {
return uintptr(unsafe.Pointer(v.native()))
}
func marshalActionGroup(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapActionGroup(wrapObject(unsafe.Pointer(c))), nil
}
func wrapActionGroup(obj *Object) *ActionGroup {
return &ActionGroup{obj}
}
// ActionGroup is a representation of GActionGroup.
type Action struct {
*Object
}
// native() returns a pointer to the underlying GActionGroup.
func (v *Action) native() *C.GAction {
if v == nil || v.GObject == nil {
return nil
}
return C.toGAction(unsafe.Pointer(v.GObject))
}
func (v *Action) Native() uintptr {
return uintptr(unsafe.Pointer(v.native()))
}
func marshalAction(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapAction(wrapObject(unsafe.Pointer(c))), nil
}
func wrapAction(obj *Object) *Action {
return &Action{obj}
}
func (v *Action) NameIsValid(name string) bool {
cstr := (*C.gchar)(C.CString(name))
defer C.free(unsafe.Pointer(cstr))
return gobool(C.g_action_name_is_valid(cstr))
}
func (v *Action) GetName() string {
c := C.g_action_get_name(v.native())
return C.GoString((*C.char)(c))
}
// XXX: g_action_get_parameter_type
// XXX: g_action_get_state_type
// XXX: g_action_get_state_hint
func (v *Action) GetEnabled() bool {
return gobool(C.g_action_get_enabled(v.native()))
}
// XXX: g_action_get_state
// XXX: g_action_change_state
func (v *Action) Activate(p *Variant) {
C.g_action_activate(v.native(), p.native())
}
// XXX: g_action_parse_detailed_name
// XXX: g_action_print_detailed_name
// ActionGroup is a representation of GActionGroup.
type SimpleAction struct {
Action
}
// native() returns a pointer to the underlying GActionGroup.
func (v *SimpleAction) native() *C.GSimpleAction {
if v == nil || v.GObject == nil {
return nil
}
return C.toGSimpleAction(unsafe.Pointer(v.GObject))
}
func (v *SimpleAction) Native() uintptr {
return uintptr(unsafe.Pointer(v.native()))
}
func marshalSimpleAction(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapSimpleAction(wrapObject(unsafe.Pointer(c))), nil
}
func wrapSimpleAction(obj *Object) *SimpleAction {
return &SimpleAction{*wrapAction(obj)}
}
func SimpleActionNew(name string, t *VariantType) *SimpleAction {
cstr := (*C.gchar)(C.CString(name))
defer C.free(unsafe.Pointer(cstr))
c := C.g_simple_action_new(cstr, t.native())
if c == nil {
return nil
}
return wrapSimpleAction(wrapObject(unsafe.Pointer(c)))
}
func (v *SimpleAction) NewStateful(name string, t *VariantType, state *Variant) *SimpleAction {
cstr := (*C.gchar)(C.CString(name))
defer C.free(unsafe.Pointer(cstr))
c := C.g_simple_action_new_stateful(cstr, t.native(), state.native())
if c == nil {
return nil
}
return wrapSimpleAction(wrapObject(unsafe.Pointer(c)))
}
func (v *SimpleAction) SetEnabled(state bool) {
C.g_simple_action_set_enabled(v.native(), gbool(state))
}
func (v *SimpleAction) SetState(state Variant) {
C.g_simple_action_set_state(v.native(), state.ToGVariant())
}
// XXX: g_simple_action_set_state_hint
// ActionGroup is a representation of GActionGroup.
type ActionMap struct {
*Object
}
// native() returns a pointer to the underlying GActionGroup.
func (v *ActionMap) native() *C.GActionMap {
if v == nil || v.GObject == nil {
return nil
}
return C.toGActionMap(unsafe.Pointer(v.GObject))
}
func (v *ActionMap) Native() uintptr {
return uintptr(unsafe.Pointer(v.native()))
}
func marshalActionMap(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapActionMap(wrapObject(unsafe.Pointer(c))), nil
}
func wrapActionMap(obj *Object) *ActionMap {
return &ActionMap{obj}
}
func (v *ActionMap) LookupAction(name string) *Action {
cstr := (*C.gchar)(C.CString(name))
defer C.free(unsafe.Pointer(cstr))
c := C.g_action_map_lookup_action(v.native(), cstr)
if c == nil {
return nil
}
return wrapAction(wrapObject(unsafe.Pointer(c)))
}
/*
// Requires GActionEntry
func (v *ActionMap) AddActionEntries() {
}
*/
func (v *ActionMap) AddAction(action *Action) {
C.g_action_map_add_action(v.native(), action.native())
}
func (v *ActionMap) RemoveAction(name string) {
cstr := (*C.gchar)(C.CString(name))
defer C.free(unsafe.Pointer(cstr))
C.g_action_map_remove_action(v.native(), cstr)
}

@ -11,6 +11,8 @@ import "unsafe"
// Application is a representation of GApplication. // Application is a representation of GApplication.
type Application struct { type Application struct {
*Object *Object
ActionMap
} }
// native() returns a pointer to the underlying GApplication. // native() returns a pointer to the underlying GApplication.
@ -31,7 +33,8 @@ func marshalApplication(p uintptr) (interface{}, error) {
} }
func wrapApplication(obj *Object) *Application { func wrapApplication(obj *Object) *Application {
return &Application{obj} am := wrapActionMap(obj)
return &Application{obj, *am}
} }
// ApplicationIDIsValid is a wrapper around g_application_id_is_valid(). // ApplicationIDIsValid is a wrapper around g_application_id_is_valid().

@ -146,10 +146,12 @@ type ApplicationFlags int
const ( const (
APPLICATION_FLAGS_NONE ApplicationFlags = C.G_APPLICATION_FLAGS_NONE APPLICATION_FLAGS_NONE ApplicationFlags = C.G_APPLICATION_FLAGS_NONE
APPLICATION_IS_SERVICE ApplicationFlags = C.G_APPLICATION_IS_SERVICE APPLICATION_IS_SERVICE ApplicationFlags = C.G_APPLICATION_IS_SERVICE
APPLICATION_IS_LAUNCHER ApplicationFlags = C.G_APPLICATION_IS_LAUNCHER
APPLICATION_HANDLES_OPEN ApplicationFlags = C.G_APPLICATION_HANDLES_OPEN APPLICATION_HANDLES_OPEN ApplicationFlags = C.G_APPLICATION_HANDLES_OPEN
APPLICATION_HANDLES_COMMAND_LINE ApplicationFlags = C.G_APPLICATION_HANDLES_COMMAND_LINE APPLICATION_HANDLES_COMMAND_LINE ApplicationFlags = C.G_APPLICATION_HANDLES_COMMAND_LINE
APPLICATION_SEND_ENVIRONMENT ApplicationFlags = C.G_APPLICATION_SEND_ENVIRONMENT APPLICATION_SEND_ENVIRONMENT ApplicationFlags = C.G_APPLICATION_SEND_ENVIRONMENT
APPLICATION_NON_UNIQUE ApplicationFlags = C.G_APPLICATION_NON_UNIQUE APPLICATION_NON_UNIQUE ApplicationFlags = C.G_APPLICATION_NON_UNIQUE
APPLICATION_CAN_OVERRIDE_APP_ID ApplicationFlags = C.G_APPLICATION_CAN_OVERRIDE_APP_ID
) )
// goMarshal is called by the GLib runtime when a closure needs to be invoked. // goMarshal is called by the GLib runtime when a closure needs to be invoked.
@ -419,6 +421,22 @@ func (v *Object) native() *C.GObject {
return C.toGObject(p) return C.toGObject(p)
} }
// Take wraps a unsafe.Pointer as a glib.Object, taking ownership of it.
// This function is exported for visibility in other gotk3 packages and
// is not meant to be used by applications.
func Take(ptr unsafe.Pointer) *Object {
obj := newObject(ToGObject(ptr))
if obj.IsFloating() {
obj.RefSink()
} else {
obj.Ref()
}
runtime.SetFinalizer(obj, (*Object).Unref)
return obj
}
// Native returns a pointer to the underlying GObject. // Native returns a pointer to the underlying GObject.
func (v *Object) Native() uintptr { func (v *Object) Native() uintptr {
return uintptr(unsafe.Pointer(v.native())) return uintptr(unsafe.Pointer(v.native()))

@ -68,6 +68,30 @@ toGApplication(void *p)
return (G_APPLICATION(p)); return (G_APPLICATION(p));
} }
static GActionGroup *
toGActionGroup(void *p)
{
return (G_ACTION_GROUP(p));
}
static GActionMap *
toGActionMap(void *p)
{
return (G_ACTION_MAP(p));
}
static GAction *
toGAction(void *p)
{
return (G_ACTION(p));
}
static GSimpleAction *
toGSimpleAction(void *p)
{
return (G_SIMPLE_ACTION(p));
}
static GSettings * static GSettings *
toGSettings(void *p) toGSettings(void *p)
{ {
@ -184,12 +208,11 @@ _g_closure_add_finalize_notifier(GClosure *closure)
static inline guint _g_signal_new(const gchar *name) { static inline guint _g_signal_new(const gchar *name) {
return g_signal_new(name, return g_signal_new(name,
G_TYPE_OBJECT, G_TYPE_OBJECT,
G_SIGNAL_RUN_FIRST, G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
0, NULL, NULL, 0, NULL, NULL,
g_cclosure_marshal_VOID__POINTER, g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, G_TYPE_NONE,
1, 0);
G_TYPE_POINTER);
} }
static void init_i18n(const char *domain, const char *dir) { static void init_i18n(const char *domain, const char *dir) {

@ -55,4 +55,5 @@ var (
VARIANT_TYPE_BYTESTRING = newVariantType(C._G_VARIANT_TYPE_BYTESTRING) VARIANT_TYPE_BYTESTRING = newVariantType(C._G_VARIANT_TYPE_BYTESTRING)
VARIANT_TYPE_BYTESTRING_ARRAY = newVariantType(C._G_VARIANT_TYPE_BYTESTRING_ARRAY) VARIANT_TYPE_BYTESTRING_ARRAY = newVariantType(C._G_VARIANT_TYPE_BYTESTRING_ARRAY)
VARIANT_TYPE_VARDICT = newVariantType(C._G_VARIANT_TYPE_VARDICT) VARIANT_TYPE_VARDICT = newVariantType(C._G_VARIANT_TYPE_VARDICT)
VARIANT_TYPE_NONE = newVariantType(nil)
) )

@ -34,6 +34,11 @@ func wrapMenuModel(obj *Object) *MenuModel {
return &MenuModel{obj} return &MenuModel{obj}
} }
// WrapMenuModel exposes wrapMenuModel
func WrapMenuModel(obj *Object) *MenuModel {
return wrapMenuModel(obj)
}
// IsMutable is a wrapper around g_menu_model_is_mutable(). // IsMutable is a wrapper around g_menu_model_is_mutable().
func (v *MenuModel) IsMutable() bool { func (v *MenuModel) IsMutable() bool {
return gobool(C.g_menu_model_is_mutable(v.native())) return gobool(C.g_menu_model_is_mutable(v.native()))

@ -40,7 +40,7 @@ func (v *AboutDialog) native() *C.GtkAboutDialog {
func marshalAboutDialog(p uintptr) (interface{}, error) { func marshalAboutDialog(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapAboutDialog(obj), nil return wrapAboutDialog(obj), nil
} }
@ -54,7 +54,7 @@ func AboutDialogNew() (*AboutDialog, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapAboutDialog(obj), nil return wrapAboutDialog(obj), nil
} }
@ -115,7 +115,7 @@ func (v *AboutDialog) GetLogo() (*gdk.Pixbuf, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
p := &gdk.Pixbuf{wrapObject(unsafe.Pointer(c))} p := &gdk.Pixbuf{glib.Take(unsafe.Pointer(c))}
return p, nil return p, nil
} }

@ -88,7 +88,7 @@ func (v *AccelGroup) native() *C.GtkAccelGroup {
func marshalAccelGroup(p uintptr) (interface{}, error) { func marshalAccelGroup(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapAccelGroup(obj), nil return wrapAccelGroup(obj), nil
} }
@ -102,7 +102,7 @@ func AccelGroupNew() (*AccelGroup, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapAccelGroup(obj), nil return wrapAccelGroup(obj), nil
} }
@ -167,7 +167,7 @@ func AccelGroupFromClosure(f interface{}) *AccelGroup {
if c == nil { if c == nil {
return nil return nil
} }
return wrapAccelGroup(wrapObject(unsafe.Pointer(c))) return wrapAccelGroup(glib.Take(unsafe.Pointer(c)))
} }
// GetModifierMask is a wrapper around gtk_accel_group_get_modifier_mask(). // GetModifierMask is a wrapper around gtk_accel_group_get_modifier_mask().
@ -214,7 +214,7 @@ func (v *AccelMap) native() *C.GtkAccelMap {
func marshalAccelMap(p uintptr) (interface{}, error) { func marshalAccelMap(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapAccelMap(obj), nil return wrapAccelMap(obj), nil
} }
@ -317,7 +317,7 @@ func AccelMapGet() *AccelMap {
if c == nil { if c == nil {
return nil return nil
} }
return wrapAccelMap(wrapObject(unsafe.Pointer(c))) return wrapAccelMap(glib.Take(unsafe.Pointer(c)))
} }
// AccelMapLockPath is a wrapper around gtk_accel_map_lock_path(). // AccelMapLockPath is a wrapper around gtk_accel_map_lock_path().
@ -347,7 +347,7 @@ func (v *Menu) GetAccelGroup() *AccelGroup {
if c == nil { if c == nil {
return nil return nil
} }
return wrapAccelGroup(wrapObject(unsafe.Pointer(c))) return wrapAccelGroup(glib.Take(unsafe.Pointer(c)))
} }
// SetAccelPath is a wrapper around gtk_menu_set_accel_path(). // SetAccelPath is a wrapper around gtk_menu_set_accel_path().

@ -61,7 +61,7 @@ func (v *ActionBar) native() *C.GtkActionBar {
func marshalActionBar(p uintptr) (interface{}, error) { func marshalActionBar(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapActionBar(wrapObject(unsafe.Pointer(c))), nil return wrapActionBar(glib.Take(unsafe.Pointer(c))), nil
} }
func wrapActionBar(obj *glib.Object) *ActionBar { func wrapActionBar(obj *glib.Object) *ActionBar {
@ -74,7 +74,7 @@ func ActionBarNew() (*ActionBar, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapActionBar(wrapObject(unsafe.Pointer(c))), nil return wrapActionBar(glib.Take(unsafe.Pointer(c))), nil
} }
//gtk_action_bar_pack_start(GtkActionBar *action_bar,GtkWidget *child) //gtk_action_bar_pack_start(GtkActionBar *action_bar,GtkWidget *child)
@ -102,5 +102,5 @@ func (a *ActionBar) GetCenterWidget() *Widget {
if w == nil { if w == nil {
return nil return nil
} }
return &Widget{glib.InitiallyUnowned{wrapObject(unsafe.Pointer(w))}} return &Widget{glib.InitiallyUnowned{glib.Take(unsafe.Pointer(w))}}
} }

@ -53,7 +53,7 @@ func (v *AppChooser) native() *C.GtkAppChooser {
func marshalAppChooser(p uintptr) (interface{}, error) { func marshalAppChooser(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapAppChooser(obj), nil return wrapAppChooser(obj), nil
} }
@ -107,7 +107,7 @@ func (v *AppChooserButton) native() *C.GtkAppChooserButton {
func marshalAppChooserButton(p uintptr) (interface{}, error) { func marshalAppChooserButton(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapAppChooserButton(wrapObject(unsafe.Pointer(c))), nil return wrapAppChooserButton(glib.Take(unsafe.Pointer(c))), nil
} }
func wrapAppChooserButton(obj *glib.Object) *AppChooserButton { func wrapAppChooserButton(obj *glib.Object) *AppChooserButton {
@ -124,7 +124,7 @@ func AppChooserButtonNew(content_type string) (*AppChooserButton, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapAppChooserButton(wrapObject(unsafe.Pointer(c))), nil return wrapAppChooserButton(glib.Take(unsafe.Pointer(c))), nil
} }
// TODO: Needs gio/GIcon implemented first // TODO: Needs gio/GIcon implemented first
@ -205,7 +205,7 @@ func (v *AppChooserWidget) native() *C.GtkAppChooserWidget {
func marshalAppChooserWidget(p uintptr) (interface{}, error) { func marshalAppChooserWidget(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapAppChooserWidget(wrapObject(unsafe.Pointer(c))), nil return wrapAppChooserWidget(glib.Take(unsafe.Pointer(c))), nil
} }
func wrapAppChooserWidget(obj *glib.Object) *AppChooserWidget { func wrapAppChooserWidget(obj *glib.Object) *AppChooserWidget {
@ -222,7 +222,7 @@ func AppChooserWidgetNew(content_type string) (*AppChooserWidget, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapAppChooserWidget(wrapObject(unsafe.Pointer(c))), nil return wrapAppChooserWidget(glib.Take(unsafe.Pointer(c))), nil
} }
// GetShowDefault() is a wrapper around gtk_app_chooser_widget_get_show_default(). // GetShowDefault() is a wrapper around gtk_app_chooser_widget_get_show_default().
@ -318,7 +318,7 @@ func (v *AppChooserDialog) native() *C.GtkAppChooserDialog {
func marshalAppChooserDialog(p uintptr) (interface{}, error) { func marshalAppChooserDialog(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapAppChooserDialog(wrapObject(unsafe.Pointer(c))), nil return wrapAppChooserDialog(glib.Take(unsafe.Pointer(c))), nil
} }
func wrapAppChooserDialog(obj *glib.Object) *AppChooserDialog { func wrapAppChooserDialog(obj *glib.Object) *AppChooserDialog {
@ -338,7 +338,7 @@ func wrapAppChooserDialog(obj *glib.Object) *AppChooserDialog {
// if c == nil { // if c == nil {
// return nil, nilPtrErr // return nil, nilPtrErr
// } // }
// return wrapAppChooserDialog(wrapObject(unsafe.Pointer(c))), nil // return wrapAppChooserDialog(glib.Take(unsafe.Pointer(c))), nil
// } // }
// AppChooserDialogNewForContentType() is a wrapper around gtk_app_chooser_dialog_new_for_content_type(). // AppChooserDialogNewForContentType() is a wrapper around gtk_app_chooser_dialog_new_for_content_type().
@ -349,13 +349,13 @@ func AppChooserDialogNewForContentType(parent *Window, flags DialogFlags, conten
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapAppChooserDialog(wrapObject(unsafe.Pointer(c))), nil return wrapAppChooserDialog(glib.Take(unsafe.Pointer(c))), nil
} }
// GetWidget() is a wrapper around gtk_app_chooser_dialog_get_widget(). // GetWidget() is a wrapper around gtk_app_chooser_dialog_get_widget().
func (v *AppChooserDialog) GetWidget() *AppChooserWidget { func (v *AppChooserDialog) GetWidget() *AppChooserWidget {
c := C.gtk_app_chooser_dialog_get_widget(v.native()) c := C.gtk_app_chooser_dialog_get_widget(v.native())
return wrapAppChooserWidget(wrapObject(unsafe.Pointer(c))) return wrapAppChooserWidget(glib.Take(unsafe.Pointer(c)))
} }
// GetHeading() is a wrapper around gtk_app_chooser_dialog_get_heading(). // GetHeading() is a wrapper around gtk_app_chooser_dialog_get_heading().

@ -42,12 +42,12 @@ func (v *Application) native() *C.GtkApplication {
func marshalApplication(p uintptr) (interface{}, error) { func marshalApplication(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapApplication(obj), nil return wrapApplication(obj), nil
} }
func wrapApplication(obj *glib.Object) *Application { func wrapApplication(obj *glib.Object) *Application {
return &Application{glib.Application{obj}} return &Application{glib.Application{obj, glib.ActionMap{obj}}}
} }
// ApplicationNew is a wrapper around gtk_application_new(). // ApplicationNew is a wrapper around gtk_application_new().
@ -59,7 +59,7 @@ func ApplicationNew(appId string, flags glib.ApplicationFlags) (*Application, er
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapApplication(wrapObject(unsafe.Pointer(c))), nil return wrapApplication(glib.Take(unsafe.Pointer(c))), nil
} }
// AddWindow is a wrapper around gtk_application_add_window(). // AddWindow is a wrapper around gtk_application_add_window().
@ -78,7 +78,7 @@ func (v *Application) GetWindowByID(id uint) *Window {
if c == nil { if c == nil {
return nil return nil
} }
return wrapWindow(wrapObject(unsafe.Pointer(c))) return wrapWindow(glib.Take(unsafe.Pointer(c)))
} }
// GetActiveWindow is a wrapper around gtk_application_get_active_window(). // GetActiveWindow is a wrapper around gtk_application_get_active_window().
@ -87,7 +87,7 @@ func (v *Application) GetActiveWindow() *Window {
if c == nil { if c == nil {
return nil return nil
} }
return wrapWindow(wrapObject(unsafe.Pointer(c))) return wrapWindow(glib.Take(unsafe.Pointer(c)))
} }
// Uninhibit is a wrapper around gtk_application_uninhibit(). // Uninhibit is a wrapper around gtk_application_uninhibit().
@ -101,7 +101,7 @@ func (v *Application) GetAppMenu() *glib.MenuModel {
if c == nil { if c == nil {
return nil return nil
} }
return &glib.MenuModel{wrapObject(unsafe.Pointer(c))} return &glib.MenuModel{glib.Take(unsafe.Pointer(c))}
} }
// SetAppMenu is a wrapper around gtk_application_set_app_menu(). // SetAppMenu is a wrapper around gtk_application_set_app_menu().
@ -116,7 +116,7 @@ func (v *Application) GetMenubar() *glib.MenuModel {
if c == nil { if c == nil {
return nil return nil
} }
return &glib.MenuModel{wrapObject(unsafe.Pointer(c))} return &glib.MenuModel{glib.Take(unsafe.Pointer(c))}
} }
// SetMenubar is a wrapper around gtk_application_set_menubar(). // SetMenubar is a wrapper around gtk_application_set_menubar().
@ -147,7 +147,7 @@ func (v *Application) GetWindows() *glib.List {
glist := C.gtk_application_get_windows(v.native()) glist := C.gtk_application_get_windows(v.native())
list := glib.WrapList(uintptr(unsafe.Pointer(glist))) list := glib.WrapList(uintptr(unsafe.Pointer(glist)))
list.DataWrapper(func(ptr unsafe.Pointer) interface{} { list.DataWrapper(func(ptr unsafe.Pointer) interface{} {
return wrapWindow(wrapObject(ptr)) return wrapWindow(glib.Take(ptr))
}) })
runtime.SetFinalizer(list, func(l *glib.List) { runtime.SetFinalizer(list, func(l *glib.List) {
l.Free() l.Free()

@ -45,5 +45,5 @@ func (v *Application) GetMenuByID(id string) *glib.Menu {
if c == nil { if c == nil {
return nil return nil
} }
return &glib.Menu{glib.MenuModel{wrapObject(unsafe.Pointer(c))}} return &glib.Menu{glib.MenuModel{glib.Take(unsafe.Pointer(c))}}
} }

@ -32,7 +32,7 @@ func (v *ApplicationWindow) native() *C.GtkApplicationWindow {
func marshalApplicationWindow(p uintptr) (interface{}, error) { func marshalApplicationWindow(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapApplicationWindow(obj), nil return wrapApplicationWindow(obj), nil
} }
@ -46,7 +46,7 @@ func ApplicationWindowNew(app *Application) (*ApplicationWindow, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapApplicationWindow(wrapObject(unsafe.Pointer(c))), nil return wrapApplicationWindow(glib.Take(unsafe.Pointer(c))), nil
} }
// SetShowMenubar is a wrapper around gtk_application_window_set_show_menubar(). // SetShowMenubar is a wrapper around gtk_application_window_set_show_menubar().

@ -50,7 +50,7 @@ func (v *ColorChooser) native() *C.GtkColorChooser {
func marshalColorChooser(p uintptr) (interface{}, error) { func marshalColorChooser(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapColorChooser(obj), nil return wrapColorChooser(obj), nil
} }
@ -127,7 +127,7 @@ func (v *ColorChooserDialog) native() *C.GtkColorChooserDialog {
func marshalColorChooserDialog(p uintptr) (interface{}, error) { func marshalColorChooserDialog(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapColorChooserDialog(wrapObject(unsafe.Pointer(c))), nil return wrapColorChooserDialog(glib.Take(unsafe.Pointer(c))), nil
} }
func wrapColorChooserDialog(obj *glib.Object) *ColorChooserDialog { func wrapColorChooserDialog(obj *glib.Object) *ColorChooserDialog {
@ -144,5 +144,5 @@ func ColorChooserDialogNew(title string, parent *Window) (*ColorChooserDialog, e
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapColorChooserDialog(wrapObject(unsafe.Pointer(c))), nil return wrapColorChooserDialog(glib.Take(unsafe.Pointer(c))), nil
} }

@ -52,7 +52,7 @@ func (v *ComboBox) toCellLayout() *C.GtkCellLayout {
func marshalComboBox(p uintptr) (interface{}, error) { func marshalComboBox(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapComboBox(obj), nil return wrapComboBox(obj), nil
} }
@ -67,7 +67,7 @@ func ComboBoxNew() (*ComboBox, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapComboBox(obj), nil return wrapComboBox(obj), nil
} }
@ -77,7 +77,7 @@ func ComboBoxNewWithEntry() (*ComboBox, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapComboBox(obj), nil return wrapComboBox(obj), nil
} }
@ -87,7 +87,7 @@ func ComboBoxNewWithModel(model ITreeModel) (*ComboBox, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapComboBox(obj), nil return wrapComboBox(obj), nil
} }
@ -141,7 +141,7 @@ func (v *ComboBox) GetModel() (*TreeModel, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapTreeModel(obj), nil return wrapTreeModel(obj), nil
} }
@ -150,6 +150,14 @@ func (v *ComboBox) SetModel(model ITreeModel) {
C.gtk_combo_box_set_model(v.native(), model.toTreeModel()) C.gtk_combo_box_set_model(v.native(), model.toTreeModel())
} }
func (v *ComboBox) Popup() {
C.gtk_combo_box_popup(v.native())
}
func (v *ComboBox) Popdown() {
C.gtk_combo_box_popdown(v.native())
}
/* /*
* GtkComboBoxText * GtkComboBoxText
*/ */
@ -170,7 +178,7 @@ func (v *ComboBoxText) native() *C.GtkComboBoxText {
func marshalComboBoxText(p uintptr) (interface{}, error) { func marshalComboBoxText(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapComboBoxText(obj), nil return wrapComboBoxText(obj), nil
} }
@ -184,7 +192,7 @@ func ComboBoxTextNew() (*ComboBoxText, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapComboBoxText(obj), nil return wrapComboBoxText(obj), nil
} }
@ -194,7 +202,7 @@ func ComboBoxTextNewWithEntry() (*ComboBoxText, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapComboBoxText(obj), nil return wrapComboBoxText(obj), nil
} }

File diff suppressed because it is too large Load Diff

@ -27,7 +27,11 @@ package gtk
// #include <stdlib.h> // #include <stdlib.h>
// #include <gtk/gtk.h> // #include <gtk/gtk.h>
import "C" import "C"
import "unsafe" import (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
// ButtonNewFromStock is a wrapper around gtk_button_new_from_stock(). // ButtonNewFromStock is a wrapper around gtk_button_new_from_stock().
func ButtonNewFromStock(stock Stock) (*Button, error) { func ButtonNewFromStock(stock Stock) (*Button, error) {
@ -37,7 +41,7 @@ func ButtonNewFromStock(stock Stock) (*Button, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapButton(wrapObject(unsafe.Pointer(c))), nil return wrapButton(glib.Take(unsafe.Pointer(c))), nil
} }
// SetUseStock is a wrapper around gtk_button_set_use_stock(). // SetUseStock is a wrapper around gtk_button_set_use_stock().
@ -77,7 +81,7 @@ func ImageNewFromStock(stock Stock, size IconSize) (*Image, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapImage(wrapObject(unsafe.Pointer(c))), nil return wrapImage(glib.Take(unsafe.Pointer(c))), nil
} }
// SetFromStock is a wrapper around gtk_image_set_from_stock(). // SetFromStock is a wrapper around gtk_image_set_from_stock().

@ -26,7 +26,11 @@ package gtk
// #cgo pkg-config: gtk+-3.0 // #cgo pkg-config: gtk+-3.0
// #include <gtk/gtk.h> // #include <gtk/gtk.h>
import "C" import "C"
import "unsafe" import (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
/* /*
* GtkDialog * GtkDialog
@ -38,7 +42,7 @@ func (v *Dialog) GetActionArea() (*Widget, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))), nil return wrapWidget(glib.Take(unsafe.Pointer(c))), nil
} }
/* /*
@ -51,7 +55,7 @@ func (v *MessageDialog) GetImage() (*Widget, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))), nil return wrapWidget(glib.Take(unsafe.Pointer(c))), nil
} }
// SetImage is a wrapper around gtk_message_dialog_set_image(). // SetImage is a wrapper around gtk_message_dialog_set_image().

@ -81,7 +81,7 @@ func AlignmentNew(xalign, yalign, xscale, yscale float32) (*Alignment, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapAlignment(obj), nil return wrapAlignment(obj), nil
} }
@ -107,7 +107,7 @@ func ArrowNew(arrowType ArrowType, shadowType ShadowType) (*Arrow, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapArrow(obj), nil return wrapArrow(obj), nil
} }
@ -185,7 +185,7 @@ func (v *Arrow) native() *C.GtkArrow {
func marshalArrow(p uintptr) (interface{}, error) { func marshalArrow(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapArrow(obj), nil return wrapArrow(obj), nil
} }
@ -213,7 +213,7 @@ func (v *Alignment) native() *C.GtkAlignment {
func marshalAlignment(p uintptr) (interface{}, error) { func marshalAlignment(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapAlignment(obj), nil return wrapAlignment(obj), nil
} }
@ -233,7 +233,7 @@ type StatusIcon struct {
func marshalStatusIcon(p uintptr) (interface{}, error) { func marshalStatusIcon(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapStatusIcon(obj), nil return wrapStatusIcon(obj), nil
} }
@ -255,7 +255,7 @@ func StatusIconNew() (*StatusIcon, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapStatusIcon(wrapObject(unsafe.Pointer(c))), nil return wrapStatusIcon(glib.Take(unsafe.Pointer(c))), nil
} }
// StatusIconNewFromFile is a wrapper around gtk_status_icon_new_from_file() // StatusIconNewFromFile is a wrapper around gtk_status_icon_new_from_file()
@ -266,7 +266,7 @@ func StatusIconNewFromFile(filename string) (*StatusIcon, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapStatusIcon(wrapObject(unsafe.Pointer(c))), nil return wrapStatusIcon(glib.Take(unsafe.Pointer(c))), nil
} }
// StatusIconNewFromIconName is a wrapper around gtk_status_icon_new_from_name() // StatusIconNewFromIconName is a wrapper around gtk_status_icon_new_from_name()
@ -277,7 +277,7 @@ func StatusIconNewFromIconName(iconName string) (*StatusIcon, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapStatusIcon(wrapObject(unsafe.Pointer(c))), nil return wrapStatusIcon(glib.Take(unsafe.Pointer(c))), nil
} }
// SetFromFile is a wrapper around gtk_status_icon_set_from_file() // SetFromFile is a wrapper around gtk_status_icon_set_from_file()
@ -410,7 +410,7 @@ func (v *Misc) native() *C.GtkMisc {
func marshalMisc(p uintptr) (interface{}, error) { func marshalMisc(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapMisc(obj), nil return wrapMisc(obj), nil
} }

@ -54,7 +54,7 @@ func goPageSetupDone(setup *C.GtkPageSetup,
delete(pageSetupDoneCallbackRegistry.m, id) delete(pageSetupDoneCallbackRegistry.m, id)
pageSetupDoneCallbackRegistry.Unlock() pageSetupDoneCallbackRegistry.Unlock()
obj := wrapObject(unsafe.Pointer(setup)) obj := glib.Take(unsafe.Pointer(setup))
r.fn(wrapPageSetup(obj), r.data) r.fn(wrapPageSetup(obj), r.data)
} }

@ -0,0 +1,26 @@
// +build !gtk_3_6,!gtk_3_8
package gtk
// #cgo pkg-config: gtk+-3.0
// #include <gtk/gtk.h>
import "C"
import (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
//export goListBoxFilterFuncs
func goListBoxFilterFuncs(row *C.GtkListBoxRow, userData C.gpointer) C.gboolean {
id := int(uintptr(userData))
listBoxFilterFuncRegistry.Lock()
r := listBoxFilterFuncRegistry.m[id]
// TODO: figure out a way to determine when we can clean up
//delete(printSettingsCallbackRegistry.m, id)
listBoxFilterFuncRegistry.Unlock()
return gbool(r.fn(wrapListBoxRow(glib.Take(unsafe.Pointer(row))), r.userData))
}

@ -12,6 +12,7 @@ package gtk
// #include "gtk_since_3_10.go.h" // #include "gtk_since_3_10.go.h"
import "C" import "C"
import ( import (
"sync"
"unsafe" "unsafe"
"github.com/gotk3/gotk3/gdk" "github.com/gotk3/gotk3/gdk"
@ -105,7 +106,7 @@ func ButtonNewFromIconName(iconName string, size IconSize) (*Button, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapButton(wrapObject(unsafe.Pointer(c))), nil return wrapButton(glib.Take(unsafe.Pointer(c))), nil
} }
/* /*
@ -141,7 +142,7 @@ func (v *HeaderBar) native() *C.GtkHeaderBar {
func marshalHeaderBar(p uintptr) (interface{}, error) { func marshalHeaderBar(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapHeaderBar(obj), nil return wrapHeaderBar(obj), nil
} }
@ -155,7 +156,7 @@ func HeaderBarNew() (*HeaderBar, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapHeaderBar(wrapObject(unsafe.Pointer(c))), nil return wrapHeaderBar(glib.Take(unsafe.Pointer(c))), nil
} }
// SetTitle is a wrapper around gtk_header_bar_set_title(). // SetTitle is a wrapper around gtk_header_bar_set_title().
@ -195,7 +196,7 @@ func (v *HeaderBar) GetCustomTitle() (*Widget, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))), nil return wrapWidget(glib.Take(unsafe.Pointer(c))), nil
} }
// PackStart is a wrapper around gtk_header_bar_pack_start(). // PackStart is a wrapper around gtk_header_bar_pack_start().
@ -254,7 +255,7 @@ func (v *ListBox) native() *C.GtkListBox {
func marshalListBox(p uintptr) (interface{}, error) { func marshalListBox(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapListBox(obj), nil return wrapListBox(obj), nil
} }
@ -268,7 +269,7 @@ func ListBoxNew() (*ListBox, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapListBox(wrapObject(unsafe.Pointer(c))), nil return wrapListBox(glib.Take(unsafe.Pointer(c))), nil
} }
// Prepend is a wrapper around gtk_list_box_prepend(). // Prepend is a wrapper around gtk_list_box_prepend().
@ -292,7 +293,7 @@ func (v *ListBox) GetSelectedRow() *ListBoxRow {
if c == nil { if c == nil {
return nil return nil
} }
return wrapListBoxRow(wrapObject(unsafe.Pointer(c))) return wrapListBoxRow(glib.Take(unsafe.Pointer(c)))
} }
// SetSelectionMode is a wrapper around gtk_list_box_set_selection_mode(). // SetSelectionMode is a wrapper around gtk_list_box_set_selection_mode().
@ -320,7 +321,7 @@ func (v *ListBox) GetActivateOnSingleClick() bool {
// GetAdjustment is a wrapper around gtk_list_box_get_adjustment(). // GetAdjustment is a wrapper around gtk_list_box_get_adjustment().
func (v *ListBox) GetAdjustment() *Adjustment { func (v *ListBox) GetAdjustment() *Adjustment {
c := C.gtk_list_box_get_adjustment(v.native()) c := C.gtk_list_box_get_adjustment(v.native())
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return &Adjustment{glib.InitiallyUnowned{obj}} return &Adjustment{glib.InitiallyUnowned{obj}}
} }
@ -340,7 +341,7 @@ func (v *ListBox) GetRowAtIndex(index int) *ListBoxRow {
if c == nil { if c == nil {
return nil return nil
} }
return wrapListBoxRow(wrapObject(unsafe.Pointer(c))) return wrapListBoxRow(glib.Take(unsafe.Pointer(c)))
} }
// GetRowAtY is a wrapper around gtk_list_box_get_row_at_y(). // GetRowAtY is a wrapper around gtk_list_box_get_row_at_y().
@ -349,7 +350,7 @@ func (v *ListBox) GetRowAtY(y int) *ListBoxRow {
if c == nil { if c == nil {
return nil return nil
} }
return wrapListBoxRow(wrapObject(unsafe.Pointer(c))) return wrapListBoxRow(glib.Take(unsafe.Pointer(c)))
} }
// InvalidateFilter is a wrapper around gtk_list_box_invalidate_filter(). // InvalidateFilter is a wrapper around gtk_list_box_invalidate_filter().
@ -367,7 +368,34 @@ func (v *ListBox) InvalidateSort() {
C.gtk_list_box_invalidate_sort(v.native()) C.gtk_list_box_invalidate_sort(v.native())
} }
// TODO: SetFilterFunc type ListBoxFilterFunc func(row *ListBoxRow, userData uintptr) bool
type listBoxFilterFuncData struct {
fn ListBoxFilterFunc
userData uintptr
}
var (
listBoxFilterFuncRegistry = struct {
sync.RWMutex
next int
m map[int]listBoxFilterFuncData
}{
next: 1,
m: make(map[int]listBoxFilterFuncData),
}
)
func (v *ListBox) SetFilterFunc(fn ListBoxFilterFunc, userData uintptr) {
listBoxFilterFuncRegistry.Lock()
id := listBoxFilterFuncRegistry.next
listBoxFilterFuncRegistry.next++
listBoxFilterFuncRegistry.m[id] = listBoxFilterFuncData{fn: fn, userData: userData}
listBoxFilterFuncRegistry.Unlock()
C._gtk_list_box_set_filter_func(v.native(), C.gpointer(uintptr(id)))
}
// TODO: SetHeaderFunc // TODO: SetHeaderFunc
// TODO: SetSortFunc // TODO: SetSortFunc
@ -396,7 +424,7 @@ func (v *ListBoxRow) native() *C.GtkListBoxRow {
func marshalListBoxRow(p uintptr) (interface{}, error) { func marshalListBoxRow(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapListBoxRow(obj), nil return wrapListBoxRow(obj), nil
} }
@ -409,7 +437,7 @@ func ListBoxRowNew() (*ListBoxRow, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapListBoxRow(wrapObject(unsafe.Pointer(c))), nil return wrapListBoxRow(glib.Take(unsafe.Pointer(c))), nil
} }
// Changed is a wrapper around gtk_list_box_row_changed(). // Changed is a wrapper around gtk_list_box_row_changed().
@ -423,7 +451,7 @@ func (v *ListBoxRow) GetHeader() *Widget {
if c == nil { if c == nil {
return nil return nil
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))) return wrapWidget(glib.Take(unsafe.Pointer(c)))
} }
// SetHeader is a wrapper around gtk_list_box_row_get_header(). // SetHeader is a wrapper around gtk_list_box_row_get_header().
@ -457,7 +485,7 @@ func (v *Revealer) native() *C.GtkRevealer {
func marshalRevealer(p uintptr) (interface{}, error) { func marshalRevealer(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapRevealer(obj), nil return wrapRevealer(obj), nil
} }
@ -471,7 +499,7 @@ func RevealerNew() (*Revealer, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapRevealer(wrapObject(unsafe.Pointer(c))), nil return wrapRevealer(glib.Take(unsafe.Pointer(c))), nil
} }
// GetRevealChild is a wrapper around gtk_revealer_get_reveal_child(). // GetRevealChild is a wrapper around gtk_revealer_get_reveal_child().
@ -534,7 +562,7 @@ func (v *SearchBar) native() *C.GtkSearchBar {
func marshalSearchBar(p uintptr) (interface{}, error) { func marshalSearchBar(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapSearchBar(obj), nil return wrapSearchBar(obj), nil
} }
@ -548,7 +576,7 @@ func SearchBarNew() (*SearchBar, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapSearchBar(wrapObject(unsafe.Pointer(c))), nil return wrapSearchBar(glib.Take(unsafe.Pointer(c))), nil
} }
// ConnectEntry is a wrapper around gtk_search_bar_connect_entry(). // ConnectEntry is a wrapper around gtk_search_bar_connect_entry().
@ -604,7 +632,7 @@ func (v *Stack) native() *C.GtkStack {
func marshalStack(p uintptr) (interface{}, error) { func marshalStack(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapStack(obj), nil return wrapStack(obj), nil
} }
@ -618,7 +646,7 @@ func StackNew() (*Stack, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapStack(wrapObject(unsafe.Pointer(c))), nil return wrapStack(glib.Take(unsafe.Pointer(c))), nil
} }
// AddNamed is a wrapper around gtk_stack_add_named(). // AddNamed is a wrapper around gtk_stack_add_named().
@ -649,7 +677,7 @@ func (v *Stack) GetVisibleChild() *Widget {
if c == nil { if c == nil {
return nil return nil
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))) return wrapWidget(glib.Take(unsafe.Pointer(c)))
} }
// SetVisibleChildName is a wrapper around gtk_stack_set_visible_child_name(). // SetVisibleChildName is a wrapper around gtk_stack_set_visible_child_name().

@ -58,4 +58,10 @@ toGtkStackSwitcher(void *p)
return (GTK_STACK_SWITCHER(p)); return (GTK_STACK_SWITCHER(p));
} }
extern gboolean goListBoxFilterFuncs (GtkListBoxRow *row,
gpointer user_data);
static inline void _gtk_list_box_set_filter_func(GtkListBox *box, gpointer user_data) {
gtk_list_box_set_filter_func(box, (GtkListBoxFilterFunc)(goListBoxFilterFuncs), user_data, NULL);
}

@ -37,7 +37,7 @@ func (v *MenuButton) GetPopover() *Popover {
if c == nil { if c == nil {
return nil return nil
} }
return wrapPopover(wrapObject(unsafe.Pointer(c))) return wrapPopover(glib.Take(unsafe.Pointer(c)))
} }
/* /*
@ -57,7 +57,7 @@ func (fb *FlowBox) native() *C.GtkFlowBox {
func marshalFlowBox(p uintptr) (interface{}, error) { func marshalFlowBox(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapFlowBox(obj), nil return wrapFlowBox(obj), nil
} }
@ -71,7 +71,7 @@ func FlowBoxNew() (*FlowBox, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapFlowBox(wrapObject(unsafe.Pointer(c))), nil return wrapFlowBox(glib.Take(unsafe.Pointer(c))), nil
} }
// Insert is a wrapper around gtk_flow_box_insert() // Insert is a wrapper around gtk_flow_box_insert()
@ -85,7 +85,7 @@ func (fb *FlowBox) GetChildAtIndex(idx int) *FlowBoxChild {
if c == nil { if c == nil {
return nil return nil
} }
return wrapFlowBoxChild(wrapObject(unsafe.Pointer(c))) return wrapFlowBoxChild(glib.Take(unsafe.Pointer(c)))
} }
// TODO 3.22.6 gtk_flow_box_get_child_at_pos() // TODO 3.22.6 gtk_flow_box_get_child_at_pos()
@ -176,7 +176,7 @@ func (fb *FlowBox) GetSelectedChildren() (rv []*FlowBoxChild) {
} }
list := glib.WrapList(uintptr(unsafe.Pointer(c))) list := glib.WrapList(uintptr(unsafe.Pointer(c)))
for l := list; l != nil; l = l.Next() { for l := list; l != nil; l = l.Next() {
o := wrapFlowBoxChild(wrapObject(l.Data().(unsafe.Pointer))) o := wrapFlowBoxChild(glib.Take(l.Data().(unsafe.Pointer)))
rv = append(rv, o) rv = append(rv, o)
} }
// We got a transfer container, so we must free the list. // We got a transfer container, so we must free the list.
@ -239,7 +239,7 @@ func (fbc *FlowBoxChild) native() *C.GtkFlowBoxChild {
func marshalFlowBoxChild(p uintptr) (interface{}, error) { func marshalFlowBoxChild(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapFlowBoxChild(obj), nil return wrapFlowBoxChild(obj), nil
} }
@ -253,7 +253,7 @@ func FlowBoxChildNew() (*FlowBoxChild, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapFlowBoxChild(wrapObject(unsafe.Pointer(c))), nil return wrapFlowBoxChild(glib.Take(unsafe.Pointer(c))), nil
} }
// GetIndex is a wrapper around gtk_flow_box_child_get_index() // GetIndex is a wrapper around gtk_flow_box_child_get_index()

@ -10,6 +10,7 @@ package gtk
import "C" import "C"
import ( import (
"unsafe" "unsafe"
"github.com/gotk3/gotk3/glib" "github.com/gotk3/gotk3/glib"
) )
@ -91,7 +92,7 @@ func (v *StackSidebar) native() *C.GtkStackSidebar {
func marshalStackSidebar(p uintptr) (interface{}, error) { func marshalStackSidebar(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapStackSidebar(obj), nil return wrapStackSidebar(obj), nil
} }
@ -105,7 +106,7 @@ func StackSidebarNew() (*StackSidebar, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapStackSidebar(wrapObject(unsafe.Pointer(c))), nil return wrapStackSidebar(glib.Take(unsafe.Pointer(c))), nil
} }
func (v *StackSidebar) SetStack(stack *Stack) { func (v *StackSidebar) SetStack(stack *Stack) {
@ -117,5 +118,11 @@ func (v *StackSidebar) GetStack() *Stack {
if c == nil { if c == nil {
return nil return nil
} }
return wrapStack(wrapObject(unsafe.Pointer(c))) return wrapStack(glib.Take(unsafe.Pointer(c)))
}
// GrabFocusWithoutSelecting is a wrapper for gtk_entry_grab_focus_without_selecting()
func (v *Entry) GrabFocusWithoutSelecting() {
C.gtk_entry_grab_focus_without_selecting(v.native())
} }

@ -0,0 +1,10 @@
// +build !gtk_3_6,!gtk_3_8,!gtk_3_10,!gtk_3_12,!gtk_3_14,!gtk_3_16,!gtk_3_18,gtk_3_20
// See: https://developer.gnome.org/gtk3/3.20/api-index-3-20.html
package gtk
// #cgo pkg-config: gtk+-3.0
// #include <gtk/gtk.h>
// #include "gtk_since_3_20.go.h"
import "C"

@ -0,0 +1,33 @@
// Same copyright and license as the rest of the files in this project
// This file contains accelerator related functions and structures
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
static GtkShortcutsWindow *
toGtkShortcutsWindow(void *p)
{
return (GTK_SHORTCUTS_WINDOW(p));
}
static GtkShortcutsSection *
toGtkShortcutsSection(void *p)
{
return (GTK_SHORTCUTS_SECTION(p));
}
static GtkShortcutsGroup *
toGtkShortcutsGroup(void *p)
{
return (GTK_SHORTCUTS_GROUP(p));
}
static GtkShortcutsShortcut *
toGtkShortcutsShortcut(void *p)
{
return (GTK_SHORTCUTS_SHORTCUT(p));
}

@ -34,7 +34,7 @@ func (v *InfoBar) native() *C.GtkInfoBar {
func marshalInfoBar(p uintptr) (interface{}, error) { func marshalInfoBar(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapInfoBar(wrapObject(unsafe.Pointer(c))), nil return wrapInfoBar(glib.Take(unsafe.Pointer(c))), nil
} }
func wrapInfoBar(obj *glib.Object) *InfoBar { func wrapInfoBar(obj *glib.Object) *InfoBar {
@ -47,7 +47,7 @@ func InfoBarNew() (*InfoBar, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapInfoBar(wrapObject(unsafe.Pointer(c))), nil return wrapInfoBar(glib.Take(unsafe.Pointer(c))), nil
} }
func (v *InfoBar) AddActionWidget(w IWidget, responseId ResponseType) { func (v *InfoBar) AddActionWidget(w IWidget, responseId ResponseType) {
@ -84,7 +84,7 @@ func (v *InfoBar) GetActionArea() (*Widget, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))), nil return wrapWidget(glib.Take(unsafe.Pointer(c))), nil
} }
func (v *InfoBar) GetContentArea() (*Box, error) { func (v *InfoBar) GetContentArea() (*Box, error) {
@ -93,7 +93,7 @@ func (v *InfoBar) GetContentArea() (*Box, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapBox(wrapObject(unsafe.Pointer(c))), nil return wrapBox(glib.Take(unsafe.Pointer(c))), nil
} }
func (v *InfoBar) GetShowCloseButton() bool { func (v *InfoBar) GetShowCloseButton() bool {

@ -34,7 +34,7 @@ func (v *Label) native() *C.GtkLabel {
func marshalLabel(p uintptr) (interface{}, error) { func marshalLabel(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapLabel(obj), nil return wrapLabel(obj), nil
} }
@ -50,7 +50,7 @@ func LabelNew(str string) (*Label, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapLabel(obj), nil return wrapLabel(obj), nil
} }
@ -234,7 +234,7 @@ func LabelNewWithMnemonic(str string) (*Label, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapLabel(obj), nil return wrapLabel(obj), nil
} }

@ -53,7 +53,7 @@ func (v *LevelBar) native() *C.GtkLevelBar {
func marshalLevelBar(p uintptr) (interface{}, error) { func marshalLevelBar(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapLevelBar(obj), nil return wrapLevelBar(obj), nil
} }
@ -67,7 +67,7 @@ func LevelBarNew() (*LevelBar, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapLevelBar(wrapObject(unsafe.Pointer(c))), nil return wrapLevelBar(glib.Take(unsafe.Pointer(c))), nil
} }
// LevelBarNewForInterval() is a wrapper around gtk_level_bar_new_for_interval(). // LevelBarNewForInterval() is a wrapper around gtk_level_bar_new_for_interval().
@ -76,7 +76,7 @@ func LevelBarNewForInterval(min_value, max_value float64) (*LevelBar, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapLevelBar(wrapObject(unsafe.Pointer(c))), nil return wrapLevelBar(glib.Take(unsafe.Pointer(c))), nil
} }
// SetMode() is a wrapper around gtk_level_bar_set_mode(). // SetMode() is a wrapper around gtk_level_bar_set_mode().

@ -32,7 +32,7 @@ func (v *MenuShell) native() *C.GtkMenuShell {
func marshalMenuShell(p uintptr) (interface{}, error) { func marshalMenuShell(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapMenuShell(obj), nil return wrapMenuShell(obj), nil
} }

@ -60,7 +60,7 @@ func (v *Popover) native() *C.GtkPopover {
func marshalPopover(p uintptr) (interface{}, error) { func marshalPopover(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapPopover(wrapObject(unsafe.Pointer(c))), nil return wrapPopover(glib.Take(unsafe.Pointer(c))), nil
} }
func wrapPopover(obj *glib.Object) *Popover { func wrapPopover(obj *glib.Object) *Popover {
@ -79,5 +79,5 @@ func PopoverNew(relative IWidget) (*Popover, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapPopover(wrapObject(unsafe.Pointer(c))), nil return wrapPopover(glib.Take(unsafe.Pointer(c))), nil
} }

@ -26,5 +26,5 @@ func (p *Popover) GetDefaultWidget() *Widget {
if w == nil { if w == nil {
return nil return nil
} }
return &Widget{glib.InitiallyUnowned{wrapObject(unsafe.Pointer(w))}} return &Widget{glib.InitiallyUnowned{glib.Take(unsafe.Pointer(w))}}
} }

@ -226,18 +226,6 @@ func marshalUnit(p uintptr) (interface{}, error) {
return Unit(c), nil return Unit(c), nil
} }
/*
* GtkPageRanges
*/
type PageRanges struct {
ranges []C.GtkPageRange
}
// free() is a wrapper around g_free.
func (pr *PageRanges) free() {
C.g_free((C.gpointer)(unsafe.Pointer(&pr.ranges[0])))
}
/* /*
* GtkPageSetup * GtkPageSetup
*/ */
@ -255,7 +243,7 @@ func (ps *PageSetup) native() *C.GtkPageSetup {
func marshalPageSetup(p uintptr) (interface{}, error) { func marshalPageSetup(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPageSetup(obj), nil return wrapPageSetup(obj), nil
} }
@ -269,7 +257,7 @@ func PageSetupNew() (*PageSetup, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPageSetup(obj), nil return wrapPageSetup(obj), nil
} }
@ -279,7 +267,7 @@ func (ps *PageSetup) Copy() (*PageSetup, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPageSetup(obj), nil return wrapPageSetup(obj), nil
} }
@ -390,7 +378,7 @@ func PageSetupNewFromFile(fileName string) (*PageSetup, error) {
defer C.g_error_free(err) defer C.g_error_free(err)
return nil, errors.New(C.GoString((*C.char)(err.message))) return nil, errors.New(C.GoString((*C.char)(err.message)))
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return &PageSetup{obj}, nil return &PageSetup{obj}, nil
} }
@ -655,7 +643,7 @@ func (pc *PrintContext) native() *C.GtkPrintContext {
func marshalPrintContext(p uintptr) (interface{}, error) { func marshalPrintContext(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPrintContext(obj), nil return wrapPrintContext(obj), nil
} }
@ -679,7 +667,7 @@ func (pc *PrintContext) SetCairoContext(cr *cairo.Context, dpiX, dpiY float64) {
// GetPageSetup() is a wrapper around gtk_print_context_get_page_setup(). // GetPageSetup() is a wrapper around gtk_print_context_get_page_setup().
func (pc *PrintContext) GetPageSetup() *PageSetup { func (pc *PrintContext) GetPageSetup() *PageSetup {
c := C.gtk_print_context_get_page_setup(pc.native()) c := C.gtk_print_context_get_page_setup(pc.native())
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPageSetup(obj) return wrapPageSetup(obj)
} }
@ -762,7 +750,7 @@ func (v *PrintOperation) toPrintOperationPreview() *C.GtkPrintOperationPreview {
func marshalPrintOperation(p uintptr) (interface{}, error) { func marshalPrintOperation(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPrintOperation(obj), nil return wrapPrintOperation(obj), nil
} }
@ -777,7 +765,7 @@ func PrintOperationNew() (*PrintOperation, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPrintOperation(obj), nil return wrapPrintOperation(obj), nil
} }
@ -805,7 +793,7 @@ func (po *PrintOperation) GetDefaultPageSetup() (*PageSetup, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPageSetup(obj), nil return wrapPageSetup(obj), nil
} }
@ -820,7 +808,7 @@ func (po *PrintOperation) GetPrintSettings(ps *PageSetup) (*PrintSettings, error
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPrintSettings(obj), nil return wrapPrintSettings(obj), nil
} }
@ -962,7 +950,7 @@ func (po *PrintOperation) GetEmbedPageSetup() bool {
// PrintRunPageSetupDialog() is a wrapper around gtk_print_run_page_setup_dialog(). // PrintRunPageSetupDialog() is a wrapper around gtk_print_run_page_setup_dialog().
func PrintRunPageSetupDialog(parent *Window, pageSetup *PageSetup, settings *PrintSettings) *PageSetup { func PrintRunPageSetupDialog(parent *Window, pageSetup *PageSetup, settings *PrintSettings) *PageSetup {
c := C.gtk_print_run_page_setup_dialog(parent.native(), pageSetup.native(), settings.native()) c := C.gtk_print_run_page_setup_dialog(parent.native(), pageSetup.native(), settings.native())
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPageSetup(obj) return wrapPageSetup(obj)
} }
@ -1027,7 +1015,7 @@ func (v *PrintOperationPreview) native() *C.GtkPrintOperationPreview {
func marshalPrintOperationPreview(p uintptr) (interface{}, error) { func marshalPrintOperationPreview(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPrintOperationPreview(obj), nil return wrapPrintOperationPreview(obj), nil
} }
@ -1077,7 +1065,7 @@ func (ps *PrintSettings) native() *C.GtkPrintSettings {
func marshalPrintSettings(p uintptr) (interface{}, error) { func marshalPrintSettings(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapPrintSettings(wrapObject(unsafe.Pointer(c))), nil return wrapPrintSettings(glib.Take(unsafe.Pointer(c))), nil
} }
func wrapPrintSettings(obj *glib.Object) *PrintSettings { func wrapPrintSettings(obj *glib.Object) *PrintSettings {
@ -1125,7 +1113,7 @@ func PrintSettingsNew() (*PrintSettings, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPrintSettings(obj), nil return wrapPrintSettings(obj), nil
} }
@ -1135,7 +1123,7 @@ func (ps *PrintSettings) Copy() (*PrintSettings, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPrintSettings(obj), nil return wrapPrintSettings(obj), nil
} }
@ -1490,26 +1478,6 @@ func (ps *PrintSettings) SetPrintPages(pages PrintPages) {
C.gtk_print_settings_set_print_pages(ps.native(), C.GtkPrintPages(pages)) C.gtk_print_settings_set_print_pages(ps.native(), C.GtkPrintPages(pages))
} }
// GetPageRanges() is a wrapper around gtk_print_settings_get_page_ranges().
func (ps *PrintSettings) GetPageRanges() (int, *PageRanges) {
var ranges *C.GtkPageRange
var num C.gint
ranges = C.gtk_print_settings_get_page_ranges(ps.native(), &num)
length := int(num)
if length == 0 {
return 0, &PageRanges{nil}
}
slice := (*[1 << 30]C.GtkPageRange)(unsafe.Pointer(ranges))[:length:length]
t := &PageRanges{slice}
runtime.SetFinalizer(t, (*PageRanges).free)
return length, t
}
// SetPageRanges() is a wrapper around gtk_print_settings_set_page_ranges().
func (ps *PrintSettings) SetPageRanges(ranges *PageRanges, num int) {
C.gtk_print_settings_set_page_ranges(ps.native(), &ranges.ranges[0], C.gint(num))
}
// GetPageSet() is a wrapper around gtk_print_settings_get_page_set(). // GetPageSet() is a wrapper around gtk_print_settings_get_page_set().
func (ps *PrintSettings) GetPageSet(pages PrintPages) PageSet { func (ps *PrintSettings) GetPageSet(pages PrintPages) PageSet {
c := C.gtk_print_settings_get_page_set(ps.native()) c := C.gtk_print_settings_get_page_set(ps.native())
@ -1596,7 +1564,7 @@ func PrintSettingsNewFromFile(name string) (*PrintSettings, error) {
defer C.g_error_free(err) defer C.g_error_free(err)
return nil, errors.New(C.GoString((*C.char)(err.message))) return nil, errors.New(C.GoString((*C.char)(err.message)))
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapPrintSettings(obj), nil return wrapPrintSettings(obj), nil
} }

@ -35,7 +35,7 @@ func (v *Settings) native() *C.GtkSettings {
func marshalSettings(p uintptr) (interface{}, error) { func marshalSettings(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
return wrapSettings(wrapObject(unsafe.Pointer(c))), nil return wrapSettings(glib.Take(unsafe.Pointer(c))), nil
} }
func wrapSettings(obj *glib.Object) *Settings { func wrapSettings(obj *glib.Object) *Settings {
@ -49,5 +49,5 @@ func SettingsGetDefault() (*Settings, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapSettings(wrapObject(unsafe.Pointer(c))), nil return wrapSettings(glib.Take(unsafe.Pointer(c))), nil
} }

@ -0,0 +1,147 @@
// +build !gtk_3_6,!gtk_3_8,!gtk_3_10,!gtk_3_12,!gtk_3_14, !gtk_3_16, !gtk_3_18
// See: https://developer.gnome.org/gtk3/3.20/api-index-3-20.html
package gtk
// #include <gtk/gtk.h>
// #include "gtk.go.h"
// #include "gtk_since_3_20.go.h"
import "C"
import (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
func init() {
tm := []glib.TypeMarshaler{
{glib.Type(C.gtk_shortcuts_window_get_type()), marshalShortcutsWindow},
{glib.Type(C.gtk_shortcuts_section_get_type()), marshalShortcutsSection},
{glib.Type(C.gtk_shortcuts_group_get_type()), marshalShortcutsGroup},
{glib.Type(C.gtk_shortcuts_shortcut_get_type()), marshalShortcutsShortcut},
}
glib.RegisterGValueMarshalers(tm)
WrapMap["GtkShortcutsWindow"] = wrapShortcutsWindow
WrapMap["GtkShortcutsSection"] = wrapShortcutsSection
WrapMap["GtkShortcutsGroup"] = wrapShortcutsGroup
WrapMap["GtkShortcutsShortcut"] = wrapShortcutsShortcut
}
/*
* GtkShortcutsWindow
*/
// ShortcutsWindow is a representation of GTK's GtkShortcutsWindow.
type ShortcutsWindow struct {
Window
}
// native returns a pointer to the underlying GtkAboutDialog.
func (v *ShortcutsWindow) native() *C.GtkShortcutsWindow {
if v == nil || v.GObject == nil {
return nil
}
p := unsafe.Pointer(v.GObject)
return C.toGtkShortcutsWindow(p)
}
func marshalShortcutsWindow(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := glib.Take(unsafe.Pointer(c))
return wrapShortcutsWindow(obj), nil
}
func wrapShortcutsWindow(obj *glib.Object) *ShortcutsWindow {
return &ShortcutsWindow{Window{Bin{Container{Widget{glib.InitiallyUnowned{obj}}}}}}
}
/*
* GtkShortcutsSection
*/
// ShortcutsWindow is a representation of GTK's GtkShortcutsWindow.
type ShortcutsSection struct {
Box
}
// native returns a pointer to the underlying GtkAboutDialog.
func (v *ShortcutsSection) native() *C.GtkShortcutsSection {
if v == nil || v.GObject == nil {
return nil
}
p := unsafe.Pointer(v.GObject)
return C.toGtkShortcutsSection(p)
}
func marshalShortcutsSection(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := glib.Take(unsafe.Pointer(c))
return wrapShortcutsSection(obj), nil
}
func wrapShortcutsSection(obj *glib.Object) *ShortcutsSection {
return &ShortcutsSection{Box{Container{Widget{glib.InitiallyUnowned{obj}}}}}
}
/*
* GtkShortcutsSection
*/
// ShortcutsWindow is a representation of GTK's GtkShortcutsWindow.
type ShortcutsGroup struct {
Box
}
// native returns a pointer to the underlying GtkAboutDialog.
func (v *ShortcutsGroup) native() *C.GtkShortcutsGroup {
if v == nil || v.GObject == nil {
return nil
}
p := unsafe.Pointer(v.GObject)
return C.toGtkShortcutsGroup(p)
}
func marshalShortcutsGroup(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := glib.Take(unsafe.Pointer(c))
return wrapShortcutsGroup(obj), nil
}
func wrapShortcutsGroup(obj *glib.Object) *ShortcutsGroup {
return &ShortcutsGroup{Box{Container{Widget{glib.InitiallyUnowned{obj}}}}}
}
/*
* GtkShortcutsShortcut
*/
// ShortcutsWindow is a representation of GTK's GtkShortcutsWindow.
type ShortcutsShortcut struct {
Box
}
// native returns a pointer to the underlying GtkAboutDialog.
func (v *ShortcutsShortcut) native() *C.GtkShortcutsShortcut {
if v == nil || v.GObject == nil {
return nil
}
p := unsafe.Pointer(v.GObject)
return C.toGtkShortcutsShortcut(p)
}
func marshalShortcutsShortcut(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := glib.Take(unsafe.Pointer(c))
return wrapShortcutsShortcut(obj), nil
}
func wrapShortcutsShortcut(obj *glib.Object) *ShortcutsShortcut {
return &ShortcutsShortcut{Box{Container{Widget{glib.InitiallyUnowned{obj}}}}}
}

@ -13,6 +13,8 @@ package gtk
import "C" import "C"
import ( import (
"unsafe" "unsafe"
"github.com/gotk3/gotk3/glib"
) )
// GetChildByName is a wrapper around gtk_stack_get_child_by_name(). // GetChildByName is a wrapper around gtk_stack_get_child_by_name().
@ -23,5 +25,5 @@ func (v *Stack) GetChildByName(name string) *Widget {
if c == nil { if c == nil {
return nil return nil
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))) return wrapWidget(glib.Take(unsafe.Pointer(c)))
} }

@ -46,7 +46,7 @@ func (v *StackSwitcher) native() *C.GtkStackSwitcher {
func marshalStackSwitcher(p uintptr) (interface{}, error) { func marshalStackSwitcher(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapStackSwitcher(obj), nil return wrapStackSwitcher(obj), nil
} }
@ -60,7 +60,7 @@ func StackSwitcherNew() (*StackSwitcher, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapStackSwitcher(wrapObject(unsafe.Pointer(c))), nil return wrapStackSwitcher(glib.Take(unsafe.Pointer(c))), nil
} }
// SetStack is a wrapper around gtk_stack_switcher_set_stack(). // SetStack is a wrapper around gtk_stack_switcher_set_stack().
@ -74,5 +74,5 @@ func (v *StackSwitcher) GetStack() *Stack {
if c == nil { if c == nil {
return nil return nil
} }
return wrapStack(wrapObject(unsafe.Pointer(c))) return wrapStack(glib.Take(unsafe.Pointer(c)))
} }

@ -64,7 +64,7 @@ func fromNativeStyleContext(c *C.GtkStyleContext) (*StyleContext, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapStyleContext(obj), nil return wrapStyleContext(obj), nil
} }
@ -107,7 +107,7 @@ func (v *StyleContext) GetScreen() (*gdk.Screen, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
d := &gdk.Screen{wrapObject(unsafe.Pointer(c))} d := &gdk.Screen{glib.Take(unsafe.Pointer(c))}
return d, nil return d, nil
} }

@ -6,7 +6,11 @@ package gtk
// #include "gtk.go.h" // #include "gtk.go.h"
import "C" import "C"
import "unsafe" import (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
/* /*
* GtkTextIter * GtkTextIter
@ -34,7 +38,7 @@ func (v *TextIter) GetBuffer() *TextBuffer {
if c == nil { if c == nil {
return nil return nil
} }
return wrapTextBuffer(wrapObject(unsafe.Pointer(c))) return wrapTextBuffer(glib.Take(unsafe.Pointer(c)))
} }
// GetOffset is a wrapper around gtk_text_iter_get_offset(). // GetOffset is a wrapper around gtk_text_iter_get_offset().

@ -44,7 +44,7 @@ func (v *TextView) native() *C.GtkTextView {
func marshalTextView(p uintptr) (interface{}, error) { func marshalTextView(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapTextView(obj), nil return wrapTextView(obj), nil
} }
@ -58,14 +58,14 @@ func TextViewNew() (*TextView, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapTextView(wrapObject(unsafe.Pointer(c))), nil return wrapTextView(glib.Take(unsafe.Pointer(c))), nil
} }
// TextViewNewWithBuffer is a wrapper around gtk_text_view_new_with_buffer(). // TextViewNewWithBuffer is a wrapper around gtk_text_view_new_with_buffer().
func TextViewNewWithBuffer(buf *TextBuffer) (*TextView, error) { func TextViewNewWithBuffer(buf *TextBuffer) (*TextView, error) {
cbuf := buf.native() cbuf := buf.native()
c := C.gtk_text_view_new_with_buffer(cbuf) c := C.gtk_text_view_new_with_buffer(cbuf)
return wrapTextView(wrapObject(unsafe.Pointer(c))), nil return wrapTextView(glib.Take(unsafe.Pointer(c))), nil
} }
// GetBuffer is a wrapper around gtk_text_view_get_buffer(). // GetBuffer is a wrapper around gtk_text_view_get_buffer().
@ -74,7 +74,7 @@ func (v *TextView) GetBuffer() (*TextBuffer, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapTextBuffer(wrapObject(unsafe.Pointer(c))), nil return wrapTextBuffer(glib.Take(unsafe.Pointer(c))), nil
} }
// SetBuffer is a wrapper around gtk_text_view_set_buffer(). // SetBuffer is a wrapper around gtk_text_view_set_buffer().
@ -335,7 +335,7 @@ func (v *TextView) GetWindow(win TextWindowType) *gdk.Window {
if c == nil { if c == nil {
return nil return nil
} }
return &gdk.Window{wrapObject(unsafe.Pointer(c))} return &gdk.Window{glib.Take(unsafe.Pointer(c))}
} }
// GetWindowType is a wrapper around gtk_text_view_get_window_type(). // GetWindowType is a wrapper around gtk_text_view_get_window_type().

@ -34,7 +34,7 @@ func (v *TreeView) native() *C.GtkTreeView {
func marshalTreeView(p uintptr) (interface{}, error) { func marshalTreeView(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapTreeView(obj), nil return wrapTreeView(obj), nil
} }
@ -47,7 +47,7 @@ func setupTreeView(c unsafe.Pointer) (*TreeView, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapTreeView(wrapObject(c)), nil return wrapTreeView(glib.Take(c)), nil
} }
// TreeViewNew() is a wrapper around gtk_tree_view_new(). // TreeViewNew() is a wrapper around gtk_tree_view_new().
@ -66,7 +66,7 @@ func (v *TreeView) GetModel() (*TreeModel, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapTreeModel(wrapObject(unsafe.Pointer(c))), nil return wrapTreeModel(glib.Take(unsafe.Pointer(c))), nil
} }
// SetModel() is a wrapper around gtk_tree_view_set_model(). // SetModel() is a wrapper around gtk_tree_view_set_model().
@ -80,7 +80,7 @@ func (v *TreeView) GetSelection() (*TreeSelection, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapTreeSelection(wrapObject(unsafe.Pointer(c))), nil return wrapTreeSelection(glib.Take(unsafe.Pointer(c))), nil
} }
// AppendColumn() is a wrapper around gtk_tree_view_append_column(). // AppendColumn() is a wrapper around gtk_tree_view_append_column().
@ -192,7 +192,7 @@ func (v *TreeView) GetColumn(n int) *TreeViewColumn {
if c == nil { if c == nil {
return nil return nil
} }
return wrapTreeViewColumn(wrapObject(unsafe.Pointer(c))) return wrapTreeViewColumn(glib.Take(unsafe.Pointer(c)))
} }
// MoveColumnAfter() is a wrapper around gtk_tree_view_move_column_after(). // MoveColumnAfter() is a wrapper around gtk_tree_view_move_column_after().
@ -211,7 +211,7 @@ func (v *TreeView) GetExpanderColumn() *TreeViewColumn {
if c == nil { if c == nil {
return nil return nil
} }
return wrapTreeViewColumn(wrapObject(unsafe.Pointer(c))) return wrapTreeViewColumn(glib.Take(unsafe.Pointer(c)))
} }
// ScrollToPoint() is a wrapper around gtk_tree_view_scroll_to_point(). // ScrollToPoint() is a wrapper around gtk_tree_view_scroll_to_point().
@ -242,7 +242,7 @@ func (v *TreeView) GetCursor() (p *TreePath, c *TreeViewColumn) {
} }
if col != nil { if col != nil {
c = wrapTreeViewColumn(wrapObject(unsafe.Pointer(col))) c = wrapTreeViewColumn(glib.Take(unsafe.Pointer(col)))
} }
return return
@ -300,7 +300,7 @@ func (v *TreeView) GetBinWindow() *gdk.Window {
return nil return nil
} }
w := &gdk.Window{wrapObject(unsafe.Pointer(c))} w := &gdk.Window{glib.Take(unsafe.Pointer(c))}
return w return w
} }
@ -330,7 +330,7 @@ func (v *TreeView) GetSearchEntry() *Entry {
if c == nil { if c == nil {
return nil return nil
} }
return wrapEntry(wrapObject(unsafe.Pointer(c))) return wrapEntry(glib.Take(unsafe.Pointer(c)))
} }
// SetSearchEntry() is a wrapper around gtk_tree_view_set_search_entry(). // SetSearchEntry() is a wrapper around gtk_tree_view_set_search_entry().

@ -32,7 +32,7 @@ func (v *TreeViewColumn) native() *C.GtkTreeViewColumn {
func marshalTreeViewColumn(p uintptr) (interface{}, error) { func marshalTreeViewColumn(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapTreeViewColumn(obj), nil return wrapTreeViewColumn(obj), nil
} }
@ -46,7 +46,7 @@ func TreeViewColumnNew() (*TreeViewColumn, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapTreeViewColumn(wrapObject(unsafe.Pointer(c))), nil return wrapTreeViewColumn(glib.Take(unsafe.Pointer(c))), nil
} }
// TreeViewColumnNewWithAttribute() is a wrapper around // TreeViewColumnNewWithAttribute() is a wrapper around
@ -62,7 +62,7 @@ func TreeViewColumnNewWithAttribute(title string, renderer ICellRenderer, attrib
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapTreeViewColumn(wrapObject(unsafe.Pointer(c))), nil return wrapTreeViewColumn(glib.Take(unsafe.Pointer(c))), nil
} }
// AddAttribute() is a wrapper around gtk_tree_view_column_add_attribute(). // AddAttribute() is a wrapper around gtk_tree_view_column_add_attribute().

@ -62,7 +62,7 @@ func (v *Widget) toWidget() *C.GtkWidget {
func marshalWidget(p uintptr) (interface{}, error) { func marshalWidget(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapWidget(obj), nil return wrapWidget(obj), nil
} }
@ -336,7 +336,7 @@ func (v *Widget) GetParent() (*Widget, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))), nil return wrapWidget(glib.Take(unsafe.Pointer(c))), nil
} }
// SetSizeRequest is a wrapper around gtk_widget_set_size_request(). // SetSizeRequest is a wrapper around gtk_widget_set_size_request().
@ -364,7 +364,7 @@ func (v *Widget) GetParentWindow() (*gdk.Window, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
w := &gdk.Window{wrapObject(unsafe.Pointer(c))} w := &gdk.Window{glib.Take(unsafe.Pointer(c))}
return w, nil return w, nil
} }
@ -456,7 +456,7 @@ func (v *Widget) GetToplevel() (*Widget, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))), nil return wrapWidget(glib.Take(unsafe.Pointer(c))), nil
} }
// GetTooltipText is a wrapper around gtk_widget_get_tooltip_text(). // GetTooltipText is a wrapper around gtk_widget_get_tooltip_text().
@ -607,7 +607,7 @@ func (v *Widget) GetWindow() (*gdk.Window, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
w := &gdk.Window{wrapObject(unsafe.Pointer(c))} w := &gdk.Window{glib.Take(unsafe.Pointer(c))}
return w, nil return w, nil
} }

@ -48,7 +48,7 @@ func (v *Window) toWindow() *C.GtkWindow {
func marshalWindow(p uintptr) (interface{}, error) { func marshalWindow(p uintptr) (interface{}, error) {
c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p))) c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapWindow(obj), nil return wrapWindow(obj), nil
} }
@ -62,7 +62,7 @@ func WindowNew(t WindowType) (*Window, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapWindow(wrapObject(unsafe.Pointer(c))), nil return wrapWindow(glib.Take(unsafe.Pointer(c))), nil
} }
// SetTitle is a wrapper around gtk_window_set_title(). // SetTitle is a wrapper around gtk_window_set_title().
@ -112,7 +112,7 @@ func (v *Window) GetScreen() (*gdk.Screen, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
s := &gdk.Screen{wrapObject(unsafe.Pointer(c))} s := &gdk.Screen{glib.Take(unsafe.Pointer(c))}
return s, nil return s, nil
} }
@ -190,7 +190,7 @@ func (v *Window) GetFocus() (*Widget, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))), nil return wrapWidget(glib.Take(unsafe.Pointer(c))), nil
} }
// SetFocus is a wrapper around gtk_window_set_focus(). // SetFocus is a wrapper around gtk_window_set_focus().
@ -204,7 +204,7 @@ func (v *Window) GetDefaultWidget() *Widget {
if c == nil { if c == nil {
return nil return nil
} }
obj := wrapObject(unsafe.Pointer(c)) obj := glib.Take(unsafe.Pointer(c))
return wrapWidget(obj) return wrapWidget(obj)
} }
@ -283,6 +283,11 @@ func (v *Window) SetDeletable(setting bool) {
C.gtk_window_set_deletable(v.native(), gbool(setting)) C.gtk_window_set_deletable(v.native(), gbool(setting))
} }
// SetTypeHint is a wrapper around gtk_window_set_type_hint().
func (v *Window) SetTypeHint(typeHint gdk.WindowTypeHint) {
C.gtk_window_set_type_hint(v.native(), C.GdkWindowTypeHint(typeHint))
}
// SetSkipTaskbarHint is a wrapper around gtk_window_set_skip_taskbar_hint(). // SetSkipTaskbarHint is a wrapper around gtk_window_set_skip_taskbar_hint().
func (v *Window) SetSkipTaskbarHint(setting bool) { func (v *Window) SetSkipTaskbarHint(setting bool) {
C.gtk_window_set_skip_taskbar_hint(v.native(), gbool(setting)) C.gtk_window_set_skip_taskbar_hint(v.native(), gbool(setting))
@ -367,7 +372,7 @@ func (v *Window) GetIcon() (*gdk.Pixbuf, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
p := &gdk.Pixbuf{wrapObject(unsafe.Pointer(c))} p := &gdk.Pixbuf{glib.Take(unsafe.Pointer(c))}
return p, nil return p, nil
} }
@ -419,7 +424,7 @@ func (v *Window) GetTransientFor() (*Window, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapWindow(wrapObject(unsafe.Pointer(c))), nil return wrapWindow(glib.Take(unsafe.Pointer(c))), nil
} }
// GetAttachedTo is a wrapper around gtk_window_get_attached_to(). // GetAttachedTo is a wrapper around gtk_window_get_attached_to().
@ -428,7 +433,13 @@ func (v *Window) GetAttachedTo() (*Widget, error) {
if c == nil { if c == nil {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapWidget(wrapObject(unsafe.Pointer(c))), nil return wrapWidget(glib.Take(unsafe.Pointer(c))), nil
}
// GetTypeHint is a wrapper around gtk_window_get_type_hint().
func (v *Window) GetTypeHint() gdk.WindowTypeHint {
c := C.gtk_window_get_type_hint(v.native())
return gdk.WindowTypeHint(c)
} }
// GetSkipTaskbarHint is a wrapper around gtk_window_get_skip_taskbar_hint(). // GetSkipTaskbarHint is a wrapper around gtk_window_get_skip_taskbar_hint().
@ -557,7 +568,7 @@ func (v *Window) GetApplication() (*Application, error) {
return nil, nilPtrErr return nil, nilPtrErr
} }
return wrapApplication(wrapObject(unsafe.Pointer(c))), nil return wrapApplication(glib.Take(unsafe.Pointer(c))), nil
} }
// SetApplication is a wrapper around gtk_window_set_application(). // SetApplication is a wrapper around gtk_window_set_application().
@ -603,7 +614,6 @@ func (v *Window) SetMnemonicModifier(mods gdk.ModifierType) {
// TODO gtk_window_get_default_icon_list(). // TODO gtk_window_get_default_icon_list().
// TODO gtk_window_get_group(). // TODO gtk_window_get_group().
// TODO gtk_window_get_icon_list(). // TODO gtk_window_get_icon_list().
// TODO gtk_window_get_type_hint().
// TODO gtk_window_get_window_type(). // TODO gtk_window_get_window_type().
// TODO gtk_window_list_toplevels(). // TODO gtk_window_list_toplevels().
// TODO gtk_window_parse_geometry(). // TODO gtk_window_parse_geometry().
@ -612,5 +622,4 @@ func (v *Window) SetMnemonicModifier(mods gdk.ModifierType) {
// TODO gtk_window_set_default_icon_list(). // TODO gtk_window_set_default_icon_list().
// TODO gtk_window_set_icon_list(). // TODO gtk_window_set_icon_list().
// TODO gtk_window_set_screen(). // TODO gtk_window_set_screen().
// TODO gtk_window_set_type_hint().
// TODO gtk_window_get_resize_grip_area(). // TODO gtk_window_get_resize_grip_area().

@ -23,8 +23,9 @@ package pango
// #include "pango.go.h" // #include "pango.go.h"
import "C" import "C"
import ( import (
"github.com/gotk3/gotk3/glib"
"unsafe" "unsafe"
"github.com/gotk3/gotk3/glib"
) )
func init() { func init() {

@ -21,10 +21,9 @@ package pango
// #include <pango/pango.h> // #include <pango/pango.h>
// #include "pango.go.h" // #include "pango.go.h"
import "C" import "C"
import (
// "github.com/andre-hub/gotk3/glib" // "github.com/andre-hub/gotk3/glib"
// "unsafe" // "unsafe"
)
func init() { func init() {

@ -0,0 +1,29 @@
language: go
build_image: shippableimages/ubuntu1404_go
go:
- tip
before_install:
- sudo apt-get update -y
- sudo apt-get install -y libglib2.0-dev libcairo2-dev libgtk-3-dev xvfb
- source $HOME/.gvm/scripts/gvm;
- if [[ $SHIPPABLE_GO_VERSION == "tip" ]]; then gvm install tip; gvm use tip; fi
- if [[ $SHIPPABLE_GO_VERSION == *release* ]]; then gvm install release; gvm use release; fi
- if [[ $SHIPPABLE_GO_VERSION =~ [0-9].[0-9] ]]; then gvm install go$SHIPPABLE_GO_VERSION; gvm use go$SHIPPABLE_GO_VERSION; fi
- export GOPATH=$SHIPPABLE_GOPATH
- go get github.com/t-yuki/gocover-cobertura
- go get github.com/onsi/gomega
- go get github.com/onsi/ginkgo
- go get code.google.com/p/go.tools/cmd/cover
- "export DISPLAY=:99.0"
- /usr/bin/Xvfb :99 &
install:
- go build -tags gtk_3_10 -v ./...
script:
- go test -tags gtk_3_10 -coverprofile=coverage_gtk.txt -covermode count ./gtk
- go test -tags gtk_3_10 ./glib
- $GOPATH/bin/gocover-cobertura < coverage_gtk.txt > shippable/codecoverage/coverage_gtk.xml
Loading…
Cancel
Save