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",
"GoVersion": "go1.7",
"GodepVersion": "v79",
"GodepVersion": "v74",
"Deps": [
{
"ImportPath": "github.com/godbus/dbus",
@ -45,15 +45,15 @@
},
{
"ImportPath": "github.com/subgraph/go-nfnetlink",
"Rev": "34abd96bd88d0fbc1a5bbba9fff1bdc2c0448f47"
"Rev": "bd5c281b400452a89af0cf298503f5d0bddc57c2"
},
{
"ImportPath": "github.com/subgraph/go-nfnetlink/nfqueue",
"Rev": "34abd96bd88d0fbc1a5bbba9fff1bdc2c0448f47"
"Rev": "bd5c281b400452a89af0cf298503f5d0bddc57c2"
},
{
"ImportPath": "github.com/subgraph/go-procsnitch",
"Rev": "fbc2965632eec2dcea9b8d630b081b10980d325d"
"Rev": "26d0071b72fb28493634fff6b2194db40114f28a"
},
{
"ImportPath": "github.com/subgraph/oz/ipc",
@ -63,6 +63,11 @@
{
"ImportPath": "github.com/subgraph/ozipc",
"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 (
"bufio"
"encoding/xml"
"fmt"
"os"
"path/filepath"
@ -18,6 +19,51 @@ const (
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 {
// this makes sure a missing definition wont break only when the app is released
uiDef := getDefinition(uiName)
@ -30,6 +76,38 @@ func getDefinitionWithFileFallback(uiName string) string {
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
func builderForDefinition(uiName string) *gtk.Builder {
// assertInUIThread()
@ -97,6 +175,10 @@ func newBuilder(uiName string) *builder {
return &builder{builderForDefinition(uiName)}
}
func newBuilderFromString(template string) *builder {
return &builder{builderForString(template)}
}
func (b *builder) getItem(name string, target interface{}) {
v := reflect.ValueOf(target)
if v.Kind() != reflect.Ptr {

@ -1,29 +1,37 @@
package main
import (
"github.com/gotk3/gotk3/gtk"
"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 redactCheck *gtk.CheckButton
var expandedCheck *gtk.CheckButton
var expertCheck *gtk.CheckButton
var actionCombo *gtk.ComboBoxText
var gridPrompt *gtk.Grid
var toplevelCheck *gtk.CheckButton
b.getItems(
fa.winb.getItems(
"level_combo", &levelCombo,
"redact_checkbox", &redactCheck,
"expanded_checkbox", &expandedCheck,
"expert_checkbox", &expertCheck,
"action_combo", &actionCombo,
"grib_gtkprompt", &gridPrompt,
"toplevel_checkbox", &toplevelCheck,
)
conf, err := dbus.getConfig()
conf, err := fa.Dbus.getConfig()
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 {
@ -35,32 +43,114 @@ func loadConfig(win *gtk.Window, b *builder, dbus *dbusObject) {
redactCheck.SetActive(v)
}
if v, ok := conf["prompt_expanded"].(bool); ok {
fa.Config.PromptExpanded = v
expandedCheck.SetActive(v)
}
if v, ok := conf["prompt_expert"].(bool); ok {
fa.Config.PromptExpert = v
expertCheck.SetActive(v)
}
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 {
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 (
"errors"
"fmt"
"os"
"strings"
"github.com/subgraph/fw-daemon/sgfw"
"github.com/godbus/dbus"
"github.com/godbus/dbus/introspect"
"github.com/gotk3/gotk3/glib"
"github.com/subgraph/fw-daemon/sgfw"
)
type dbusObject struct {
dbus.BusObject
}
type dbusObjectP struct {
dbus.BusObject
}
type dbusServer struct {
conn *dbus.Conn
run bool
@ -29,14 +31,6 @@ func newDbusObject() (*dbusObject, error) {
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) {
var flag bool
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) {
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) {
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) {
@ -78,14 +106,109 @@ func (ob *dbusObject) setConfig(key string, val interface{}) {
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()
if err != nil {
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 {
return nil, err
@ -97,21 +220,39 @@ func newDbusServer() (*dbusServer, error) {
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
}
ds.conn = conn
ds.run = true
return ds, nil
}
func (ds *dbusServer) Alert(data string) *dbus.Error {
fmt.Println("Received Dbus update alert: ", data)
glib.IdleAdd(repopulateWin)
return nil
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,
is_socks bool, timestamp string, optstring string, expanded, expert bool, action int32) (bool, *dbus.Error) {
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)
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) {
ob.Call("com.subgraph.fwprompt.EventNotifier.Alert", 0, data)
func (ds *dbusServer) RemovePrompt(guid string) *dbus.Error {
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 -->
<interface>
<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="title">Subgraph Firewall</property>
<property name="window_position">center</property>
<property name="default_width">600</property>
<property name="default_height">400</property>
<property name="default_width">700</property>
<property name="default_height">600</property>
<child>
<object class="GtkBox" id="box1">
<property name="can_focus">False</property>
@ -18,7 +28,56 @@
<object class="GtkStack" id="toplevel_stack">
<property name="can_focus">False</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>
<object class="GtkNotebook" id="rulesnotebook">
<property name="visible">True</property>
@ -127,14 +186,60 @@
<property name="tab_fill">False</property>
</packing>
</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>
<packing>
<property name="name">page0</property>
<property name="name">rules</property>
<property name="title" translatable="yes">Rules</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid1">
<object class="GtkGrid" id="config_grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
@ -208,7 +313,6 @@
<item id="info" translatable="yes">Info</item>
<item id="debug" translatable="yes">Debug</item>
</items>
<signal name="changed" handler="on_level_combo_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@ -224,7 +328,6 @@
<property name="halign">start</property>
<property name="margin_left">10</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_redact_checkbox_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
@ -241,7 +344,6 @@
<property name="halign">start</property>
<property name="margin_left">10</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_expanded_checkbox_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
@ -258,7 +360,6 @@
<property name="halign">start</property>
<property name="margin_left">10</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_expert_checkbox_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
@ -276,7 +377,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
@ -288,20 +389,49 @@
<items>
<item id="FOREVER" translatable="yes">Forever</item>
<item id="SESSION" translatable="yes">Session</item>
<item id="PROCESS" translatable="yes">Process</item>
<item id="ONCE" translatable="yes">Once</item>
</items>
<signal name="changed" handler="on_action_combo_changed" swapped="no"/>
</object>
<packing>
<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="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="name">page1</property>
<property name="name">config</property>
<property name="title" translatable="yes">Options</property>
<property name="position">1</property>
<property name="position">2</property>
</packing>
</child>
</object>
@ -318,12 +448,83 @@
<property name="can_focus">False</property>
<property name="show_close_button">True</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">
<object class="GtkStackSwitcher" id="stack_switcher">
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="icon_size">2</property>
<property name="stack">toplevel_stack</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
</object>
</child>

@ -6,13 +6,12 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="row_homogeneous">True</property>
<child>
<object class="GtkLabel" id="app_label">
<object class="GtkImage" id="app_icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">8</property>
<property name="margin_right">10</property>
<property name="stock">gtk-missing-image</property>
</object>
<packing>
<property name="left_attach">0</property>
@ -20,11 +19,13 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="verb_label">
<object class="GtkLabel" id="app_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">8</property>
<property name="margin_right">10</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">1</property>
@ -32,39 +33,28 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="origin_label">
<object class="GtkImage" id="verb_icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_right">10</property>
<property name="stock">gtk-no</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</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">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_right">10</property>
<property name="hexpand">True</property>
<property name="xpad">10</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">4</property>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
@ -85,45 +75,45 @@
</child>
</object>
<packing>
<property name="left_attach">5</property>
<property name="left_attach">4</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="delete_button">
<property name="visible">True</property>
<object class="GtkButton" id="save_button">
<property name="sensitive">False</property>
<property name="can_focus">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>
<signal name="clicked" handler="on_delete_rule" swapped="no"/>
<signal name="clicked" handler="on_save_rule" swapped="no"/>
<child>
<object class="GtkImage" id="image1">
<object class="GtkImage" id="img_save_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-delete-symbolic</property>
<property name="icon_name">document-new-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">7</property>
<property name="left_attach">5</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="save_button">
<property name="sensitive">False</property>
<object class="GtkButton" id="delete_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">Save as new permanent firewall rule</property>
<property name="tooltip_text" translatable="yes">Delete firewall rule</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>
<object class="GtkImage" id="img_save_button">
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">document-new-symbolic</property>
<property name="icon_name">edit-delete-symbolic</property>
</object>
</child>
</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 -->
<interface>
<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="title">Subgraph Firewall</property>
<property name="window_position">center</property>
<property name="default_width">600</property>
<property name="default_height">400</property>
<property name="default_width">700</property>
<property name="default_height">600</property>
<child>
<object class="GtkBox" id="box1">
<property name="can_focus">False</property>
@ -28,7 +38,56 @@ func (*defDialog) String() string {
<object class="GtkStack" id="toplevel_stack">
<property name="can_focus">False</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>
<object class="GtkNotebook" id="rulesnotebook">
<property name="visible">True</property>
@ -137,14 +196,60 @@ func (*defDialog) String() string {
<property name="tab_fill">False</property>
</packing>
</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>
<packing>
<property name="name">page0</property>
<property name="name">rules</property>
<property name="title" translatable="yes">Rules</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid1">
<object class="GtkGrid" id="config_grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
@ -218,7 +323,6 @@ func (*defDialog) String() string {
<item id="info" translatable="yes">Info</item>
<item id="debug" translatable="yes">Debug</item>
</items>
<signal name="changed" handler="on_level_combo_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@ -234,7 +338,6 @@ func (*defDialog) String() string {
<property name="halign">start</property>
<property name="margin_left">10</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_redact_checkbox_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
@ -251,7 +354,6 @@ func (*defDialog) String() string {
<property name="halign">start</property>
<property name="margin_left">10</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_expanded_checkbox_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
@ -268,7 +370,6 @@ func (*defDialog) String() string {
<property name="halign">start</property>
<property name="margin_left">10</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_expert_checkbox_toggled" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
@ -286,7 +387,7 @@ func (*defDialog) String() string {
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
<property name="top_attach">7</property>
</packing>
</child>
<child>
@ -301,18 +402,46 @@ func (*defDialog) String() string {
<item id="PROCESS" translatable="yes">Process</item>
<item id="ONCE" translatable="yes">Once</item>
</items>
<signal name="changed" handler="on_action_combo_changed" swapped="no"/>
</object>
<packing>
<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="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="name">page1</property>
<property name="name">config</property>
<property name="title" translatable="yes">Options</property>
<property name="position">1</property>
<property name="position">2</property>
</packing>
</child>
</object>
@ -329,12 +458,83 @@ func (*defDialog) String() string {
<property name="can_focus">False</property>
<property name="show_close_button">True</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">
<object class="GtkStackSwitcher" id="stack_switcher">
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="icon_size">2</property>
<property name="stack">toplevel_stack</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
</object>
</child>

@ -16,13 +16,12 @@ func (*defRuleItem) String() string {
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="row_homogeneous">True</property>
<child>
<object class="GtkLabel" id="app_label">
<object class="GtkImage" id="app_icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">8</property>
<property name="margin_right">10</property>
<property name="stock">gtk-missing-image</property>
</object>
<packing>
<property name="left_attach">0</property>
@ -30,11 +29,13 @@ func (*defRuleItem) String() string {
</packing>
</child>
<child>
<object class="GtkLabel" id="verb_label">
<object class="GtkLabel" id="app_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">8</property>
<property name="margin_right">10</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">1</property>
@ -42,38 +43,28 @@ func (*defRuleItem) String() string {
</packing>
</child>
<child>
<object class="GtkLabel" id="origin_label">
<object class="GtkImage" id="verb_icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_right">10</property>
<property name="stock">gtk-no</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</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>
<object class="GtkLabel" id="target_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_right">10</property>
<property name="hexpand">True</property>
<property name="xpad">10</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">4</property>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
@ -94,45 +85,45 @@ func (*defRuleItem) String() string {
</child>
</object>
<packing>
<property name="left_attach">5</property>
<property name="left_attach">4</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="delete_button">
<property name="visible">True</property>
<object class="GtkButton" id="save_button">
<property name="sensitive">False</property>
<property name="can_focus">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>
<signal name="clicked" handler="on_delete_rule" swapped="no"/>
<signal name="clicked" handler="on_save_rule" swapped="no"/>
<child>
<object class="GtkImage" id="image1">
<object class="GtkImage" id="img_save_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">edit-delete-symbolic</property>
<property name="icon_name">document-new-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">7</property>
<property name="left_attach">5</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="save_button">
<property name="sensitive">False</property>
<object class="GtkButton" id="delete_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">Save as new permanent firewall rule</property>
<property name="tooltip_text" translatable="yes">Delete firewall rule</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>
<object class="GtkImage" id="img_save_button">
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">document-new-symbolic</property>
<property name="icon_name">edit-delete-symbolic</property>
</object>
</child>
</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) {
return false
}
if !isValidPort(port) {
if !isValidPort(port, "") {
return false
}
return true
@ -113,7 +113,8 @@ func isValidHost(host string) bool {
return true
}
func isValidPort(port string) bool {
func isValidPort(port, proto string) bool {
min := 0
if port == "*" {
return true
}
@ -122,7 +123,11 @@ func isValidPort(port string) bool {
if err != nil {
return false
}
return pval > 0 && pval <= 0xFFFF
if proto == "icmp" {
min = -1
}
return pval > min && pval <= 0xFFFF
}
func (re *ruleEdit) updateRow() {
@ -145,15 +150,15 @@ func (re *ruleEdit) updateRow() {
}
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 saveasnew {
re.row.rule.Mode = uint16(sgfw.RULE_MODE_PERMANENT)
}
re.updateRow()
re.row.rl.dbus.updateRule(re.row.rule)
re.row.rl.app.Dbus.updateRule(re.row.rule)
if saveasnew {
re.row.widget.Hide()
re.row.Hide()
}
}
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 (
"fmt"
"os"
"strconv"
"strings"
"sync"
"github.com/subgraph/fw-daemon/sgfw"
"github.com/gotk3/gotk3/gtk"
"github.com/gotk3/gotk3/glib"
)
type ruleList struct {
dbus *dbusObject
win *gtk.Window
lock *sync.Mutex
app *fwApp
mode sgfw.RuleMode
rows []*ruleRow
rules []sgfw.DbusRule
rowsByIndex map[int]*ruleRow
list *gtk.ListBox
col0 *gtk.SizeGroup
col1 *gtk.SizeGroup
col2 *gtk.SizeGroup
col3 *gtk.SizeGroup
col4 *gtk.SizeGroup
col5 *gtk.SizeGroup
raHandlerID glib.SignalHandle
}
type ruleRow struct {
*gtk.ListBoxRow
rl *ruleList
rule *sgfw.DbusRule
widget *gtk.ListBoxRow
gtkBox *gtk.Box
gtkSep *gtk.Separator
gtkGrid *gtk.Grid
gtkLabelApp *gtk.Label
gtkLabelVerb *gtk.Label
gtkLabelOrigin *gtk.Label
gtkLabelPrivs *gtk.Label
gtkLabelTarget *gtk.Label
gtkButtonEdit *gtk.Button
gtkButtonSave *gtk.Button
gtkButtonDelete *gtk.Button
gtkAppIcon *gtk.Image
gtkIconVerb *gtk.Image
}
func newRuleList(dbus *dbusObject, win *gtk.Window, list *gtk.ListBox) *ruleList {
rl := &ruleList{dbus: dbus, win: win, list: list}
func newRuleList(app *fwApp, list *gtk.ListBox, mode sgfw.RuleMode) *ruleList {
rl := &ruleList{app: app, list: list}
rl.lock = new(sync.Mutex)
rl.mode = mode
rl.list.SetSelectionMode(gtk.SELECTION_NONE)
rl.col0, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col1, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col2, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col3, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col4, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col5, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.list.SetActivateOnSingleClick(false)
return rl
}
func (rl *ruleList) loadRules(mode sgfw.RuleMode) error {
rules, err := rl.dbus.listRules()
func (rl *ruleList) loadRules(noAdd bool) error {
rl.lock.Lock()
defer rl.lock.Unlock()
rules, err := rl.app.Dbus.listRules()
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %+v\n", 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
}
func (rl *ruleList) addRules(rules []sgfw.DbusRule, mode sgfw.RuleMode) {
for i := 0; i < len(rules); i++ {
if sgfw.RuleMode(rules[i].Mode) != mode {
continue
func (rl *ruleList) reloadRules(filter string) {
rl.lock.Lock()
defer rl.lock.Unlock()
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])
row.rl = rl
rl.rows = rl.rows[:0]
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.col2.AddWidget(row.gtkLabelVerb)
rl.col3.AddWidget(row.gtkLabelOrigin)
rl.col4.AddWidget(row.gtkLabelPrivs)
rl.col5.AddWidget(row.gtkLabelTarget)
rl.list.Add(row.widget)
rl.col2.AddWidget(row.gtkIconVerb)
rl.col3.AddWidget(row.gtkLabelTarget)
rl.list.Add(row)
rl.rowsByIndex[row.GetIndex()] = row
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 {
row := &ruleRow{}
func (rl *ruleList) createWidget(rule *sgfw.DbusRule) *ruleRow {
row := &ruleRow{rl: rl}
row.rule = rule
builder := newBuilder("RuleItem")
var grid *gtk.Grid
builder.getItems(
"grid", &grid,
"grid", &row.gtkGrid,
"app_label", &row.gtkLabelApp,
"verb_label", &row.gtkLabelVerb,
"origin_label", &row.gtkLabelOrigin,
"privs_label", &row.gtkLabelPrivs,
"verb_icon", &row.gtkIconVerb,
"target_label", &row.gtkLabelTarget,
"edit_button", &row.gtkButtonEdit,
"save_button", &row.gtkButtonSave,
"delete_button", &row.gtkButtonDelete,
"app_icon", &row.gtkAppIcon,
)
switch sgfw.RuleMode(rule.Mode) {
case sgfw.RULE_MODE_SYSTEM:
@ -96,6 +182,10 @@ func createWidget(rule *sgfw.DbusRule) *ruleRow {
row.gtkButtonDelete.SetSensitive(false)
row.gtkButtonDelete.SetTooltipText("Cannot delete system rules")
break
case sgfw.RULE_MODE_PROCESS:
row.gtkButtonSave.SetSensitive(true)
row.gtkButtonSave.SetNoShowAll(false)
break
case sgfw.RULE_MODE_SESSION:
row.gtkButtonSave.SetSensitive(true)
row.gtkButtonSave.SetNoShowAll(false)
@ -107,28 +197,104 @@ func createWidget(rule *sgfw.DbusRule) *ruleRow {
"on_save_rule": row.onSaveAsNew,
"on_delete_rule": row.onDelete,
})
row.widget, _ = gtk.ListBoxRowNew()
row.widget.Add(grid)
row.gtkBox, _ = gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
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()
//builder.Object.Unref()
builder = nil
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() {
if rr.rule.Mode == uint16(sgfw.RULE_MODE_PROCESS) {
appstr := "(" + strconv.Itoa(int(rr.rule.Pid)) + ") " + rr.rule.App
rr.gtkLabelApp.SetText(appstr)
rr.gtkLabelApp.SetTooltipText(rr.rule.Path)
rr.setVerbIcon()
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 {
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)
}
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)
rr.gtkLabelVerb.SetText(getVerbText(rr.rule))
if rr.rule.Proto == "tcp" {
rr.gtkLabelOrigin.SetText(rr.rule.Origin)
} else {
rr.gtkLabelOrigin.SetText(rr.rule.Origin + " (" + rr.rule.Proto + ")")
if err != nil {
fmt.Println("Error getting icon theme.")
return
}
pb, _ := it.LoadIcon(in, int(gtk.ICON_SIZE_BUTTON), gtk.ICON_LOOKUP_USE_BUILTIN)
if pb == nil {
fmt.Println("Error getting icon theme.")
return
}
rr.gtkLabelPrivs.SetText(rr.rule.Privs)
rr.gtkLabelTarget.SetText(getTargetText(rr.rule))
rr.gtkIconVerb.SetFromIconName(in, gtk.ICON_SIZE_BUTTON)
rr.gtkIconVerb.SetTooltipText(tt)
}
func getVerbText(rule *sgfw.DbusRule) string {
@ -142,44 +308,73 @@ func getVerbText(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 == "*:*" {
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 {
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 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" {
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 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, " ")
}
if rule.Proto == "tcp" {
return fmt.Sprintf("Connections to %s on port %s", items[0], items[1])
} else if rule.Proto == "icmp" {
return fmt.Sprintf("Data to %s with ICMP code %s", items[0], items[1])
ps := "port"
if rule.Proto == "icmp" {
ps = "code"
}
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() {
rr.runEditor(true)
rr.runNewEditor(DIALOG_MODE_SAVEAS)
}
func (rr *ruleRow) onEdit() {
rr.runEditor(false)
rr.runNewEditor(DIALOG_MODE_EDIT)
}
func (rr *ruleRow) onDelete() {
@ -187,22 +382,22 @@ func (rr *ruleRow) onDelete() {
if rr.rule.Sandbox != "" {
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`
body = fmt.Sprintf(ss, rr.rule.Path, rr.rule.Sandbox, getVerbText(rr.rule), getTargetText(rr.rule))
<b>Rule:</b> %s`
body = fmt.Sprintf(ss, rr.rule.Path, rr.rule.Sandbox, getTargetText(rr.rule))
} else {
ss := `Are you sure you want to delete this rule:
<b>Path:</b> %s
<b>Path:</b> %s
<b>Rule:</b> %s %s`
body = fmt.Sprintf(ss, rr.rule.Path, getVerbText(rr.rule), getTargetText(rr.rule))
<b>Rule:</b> %s`
body = fmt.Sprintf(ss, rr.rule.Path, getTargetText(rr.rule))
}
d := gtk.MessageDialogNewWithMarkup(
rr.rl.win,
rr.rl.app.win,
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_QUESTION,
gtk.BUTTONS_OK_CANCEL,
@ -212,19 +407,37 @@ func (rr *ruleRow) onDelete() {
rr.delete()
}
d.Destroy()
}
func (rl *ruleList) remove(rr *ruleRow) {
rl.col0.RemoveWidget(rr.gtkAppIcon)
rl.col1.RemoveWidget(rr.gtkLabelApp)
rl.col2.RemoveWidget(rr.gtkLabelVerb)
rl.col3.RemoveWidget(rr.gtkLabelOrigin)
rl.col4.RemoveWidget(rr.gtkLabelPrivs)
rl.col5.RemoveWidget(rr.gtkLabelTarget)
rl.list.Remove(rr.widget)
rl.col2.RemoveWidget(rr.gtkIconVerb)
rl.col3.RemoveWidget(rr.gtkLabelTarget)
rl.list.Remove(rr.ListBoxRow)
for i := (len(rl.rules) - 1); i >= 0; i-- {
if *rr.rule == rl.rules[i] {
rl.rules = append(rl.rules[:i], rl.rules[i+1:]...)
break;
}
}
}
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.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 St = imports.gi.St;
const CheckBox = imports.ui.checkBox
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
const RuleScope = {
APPLY_ONCE: 0,
APPLY_SESSION: 1,
APPLY_PROCESS: 2,
APPLY_FOREVER: 3,
APPLY_SESSION: 0,
APPLY_PROCESS: 1,
APPLY_PERMANENT: 2,
APPLY_SYSTEM: 3,
APPLY_ONCE: 4,
};
const DetailSection = new Lang.Class({
@ -22,7 +24,7 @@ const DetailSection = new Lang.Class({
_init: function(sandboxed) {
this.actor = new St.BoxLayout({ style_class: 'fw-details-section' });
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._right);
@ -30,8 +32,8 @@ const DetailSection = new Lang.Class({
this.path = this._addDetails("Path:");
this.pid = this._addDetails("Process ID:");
this.origin = this._addDetails("Origin:");
this.user = this._addDetails("User:");
this.group = this._addDetails("Group:");
this.user = this._addCheckboxDetails("User:");
this.group = this._addCheckboxDetails("Group:");
this.sandboxed = sandboxed;
if (sandboxed) {
@ -40,15 +42,43 @@ const DetailSection = new Lang.Class({
this.optstring = this._addDetails("");
},
_addDetails: function(text) {
_addDetails: function(text, d) {
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});
let msg = new St.Label({ style_class: 'fw-detail-message' });
if (d === undefined) {
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;
},
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.path.text = path;
@ -60,22 +90,28 @@ const DetailSection = new Lang.Class({
this.origin.text = origin;
if (expert === true) {
this.user[0].show();
this.user[1].actor.show();
if (user != "") {
this.user.text = user;
this.user[1].getLabelActor().text = user;
if (uid != -1) {
this.user.text += " (" + uid.toString() + ")";
this.user[1].getLabelActor().text += " (" + uid.toString() + ")";
}
} 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 != "") {
this.group.text = group;
this.group[1].getLabelActor().text = group;
if (gid != -1) {
this.group.text += " (" + gid.toString() + ")";
this.group[1].getLabelActor().text += " (" + gid.toString() + ")";
}
} else {
this.group.text = "gid:" + gid.toString();
this.group[1].getLabelActor().text = "gid:" + gid.toString();
}
}
if (sandbox != "") {
@ -142,9 +178,9 @@ const OptionList = new Lang.Class({
_init: function(pid_known, sandboxed) {
this.actor = new St.BoxLayout({vertical: true, style_class: 'fw-option-list'});
if (pid_known) {
this.buttonGroup = new ButtonGroup("Forever", "Session", "Once", "PID");
this.buttonGroup = new ButtonGroup("Permanent", "Session", "Process", "Once");
} else {
this.buttonGroup = new ButtonGroup("Forever", "Session", "Once");
this.buttonGroup = new ButtonGroup("Permanent", "Session", "Once");
}
this.actor.add_child(this.buttonGroup.actor);
this.items = [];
@ -224,13 +260,13 @@ const OptionList = new Lang.Class({
selectedScope: function() {
switch(this.buttonGroup._checked) {
case 0:
return RuleScope.APPLY_FOREVER;
return RuleScope.APPLY_PERMANENT;
case 1:
return RuleScope.APPLY_SESSION;
case 2:
return RuleScope.APPLY_ONCE;
case 3:
return RuleScope.APPLY_PROCESS;
case 3:
return RuleScope.APPLY_ONCE;
default:
log("SGFW: unexpected scope value "+ this.buttonGroup._selected);
return RuleScope.APPLY_SESSION;
@ -238,14 +274,17 @@ const OptionList = new Lang.Class({
},
scopeToIdx: function(scope) {
if (scope === undefined) {
scope = this.buttonGroup._checked;
}
switch (scope) {
case RuleScope.APPLY_PROCESS:
return 3;
case RuleScope.APPLY_ONCE:
return 3;
case RuleScope.APPLY_PROCESS:
return 2;
case RuleScope.APPLY_SESSION:
return 1;
case RuleScope.APPLY_FOREVER:
case RuleScope.APPLY_PERMANENT:
return 0;
default:
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() {
this.buttonGroup.next();
},
@ -484,12 +544,22 @@ const PromptDialogHeader = new Lang.Class({
_init: function() {
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.title = new St.Label({style_class: 'fw-prompt-title'})
this.message = new St.Label({style_class: 'fw-prompt-message'});
this.message.clutter_text.line_wrap = true;
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.message);
this.actor.add_child(this.icon);
@ -521,16 +591,27 @@ const PromptDialogHeader = new Lang.Class({
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({
Name: 'PromptDialog',
Extends: ModalDialog.ModalDialog,
_init: function(invocation, pid_known, sandboxed, tlsguard, cbClose) {
_init: function(guid, timestamp, pid_known, sandboxed, tlsguard, cbClose) {
this.cbClose = cbClose;
this.parent({ styleClass: 'fw-prompt-dialog' });
this._invocation = invocation;
this._guid = guid;
this._timestamp = timestamp;
this._expert = false;
this.header = new PromptDialogHeader();
this.contentLayout.add_child(this.header.actor);
@ -552,7 +633,20 @@ const PromptDialog = new Lang.Class({
if (tlsguard) {
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.setButtons([
{ label: "Allow", action: Lang.bind(this, this.onAllow) },
@ -560,6 +654,10 @@ const PromptDialog = new Lang.Class({
]);
},
activate: function() {
this.open();
},
_onPromptScopeNext: function() {
if (this.details.isOpen) {
this.optionList.scopeNext();
@ -603,23 +701,22 @@ const PromptDialog = new Lang.Class({
},
onAllow: function() {
if (this.cbClose !== undefined && this.cbClose !== null) {
this.cbClose();
}
this.close();
this.sendReturnValue(true);
},
onDeny: function() {
if (this.cbClose !== undefined && this.cbClose !== null) {
this.cbClose();
}
this.close();
this.sendReturnValue(false);
},
/*
onToggleTLSGuard: function(item) {
log("SGFW: Toggle TLS Guard: " + item);
},
*/
sendReturnValue: function(allow) {
if (!this._invocation) {
if (!this._guid || this.cbClose === undefined || this.cbClose === null) {
return;
}
let verb = "DENY";
@ -631,18 +728,24 @@ const PromptDialog = new Lang.Class({
verb = "ALLOW";
}
}
let rule = verb + "|" + this.ruleTarget() + "|" + this.ruleSandbox();
let scope = this.optionList.selectedScope();
this._invocation.return_value(GLib.Variant.new('(is)', [scope, rule]));
this._invocation = null;
//ALLOW|tcp:textsecure-service-ca.whispersystems.org:4433|-1:-1|PERMANENT
//DENY|subgraph.com:8183|-1:-1|0
let privs = ["-1", "-1"];
if (this.info.user[1].actor.checked === true) {
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() {
let base = "";
if (this._proto != "tcp") {
//if (this._proto != "tcp") {
base = this._proto + ":";
}
//}
switch(this.optionList.selectedIdx()) {
case 0:
return base + this._address + ":" + this._port;
@ -669,6 +772,8 @@ const PromptDialog = new Lang.Class({
this._proto = proto;
this._sandbox = sandbox;
this._tlsGuard = tlsguard;
this._expert = expert;
this._privs = {"uid": uid, "gid": gid};
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);
}
if (expert) {
if (this._expert) {
if (proto == "icmp") {
this.optionList.setOptionText(1, "Only "+ address + " with any ICMP code");
} else if (proto == "udp") {
@ -727,6 +832,11 @@ const PromptDialog = new Lang.Class({
}
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 Mainloop = imports.mainloop;
const Main = imports.ui.main;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Extension = imports.misc.extensionUtils.getCurrentExtension();
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> \
<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="icon" /> \
<arg type="s" direction="in" name="path" /> \
@ -64,15 +67,17 @@ const FirewallPromptInterface = '<node> \
<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="i" direction="out" name="scope" /> \
<arg type="s" direction="out" name="rule" /> \
<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> \
<method name="ClosePrompt"/> \
<method name="TestPrompt"/> \
</interface> \
</node>';
@ -84,8 +89,13 @@ const FirewallPromptHandler = new Lang.Class({
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FirewallPromptInterface, this);
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);
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.RequestPendingPrompts();
},
destroy: function() {
@ -93,6 +103,10 @@ const FirewallPromptHandler = new Lang.Class({
this._closeDialogs();
this._dbusImpl.unexport();
this._destroyKeybindings();
if (this._timeoutId !== null) {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = null;
}
},
_initKeybindings: function() {
@ -116,71 +130,187 @@ const FirewallPromptHandler = new Lang.Class({
},
_handleKeybinding: function(a, b, c, d, binding) {
if (this._dialogs.length <= 0) {
if (this._dialog === null) {
return false;
}
let fname = binding.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
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);
return true;
}
let fn = this._dialogs[0][fname];
let fn = this._dialog[fname];
if (typeof fn !== "function") {
log("SGFW: Invalid key binding (2)... " + fname + " " + (typeof fn));
return true;
}
Lang.bind(this._dialogs[0], fn)();
Lang.bind(this._dialog, fn)();
return true;
},
_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]);
}
},
_closeDialogs: function() {
log("SGFW: Closing all dialogs");
if (this._dialogs.length > 0) {
dialog = this._dialogs.shift();
dialog.close();
if (this._dialog !== null) {
this._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) {
let [app, icon, path, address, port, ip, origin, proto, uid, gid, user, group, pid, sandbox, tlsguard, optstring, expanded, expert, action] = params;
let cbfn = function(self) {
return function() { return self.onCloseDialog(); }
}(this)
RequestPromptAsyncAsync: function(params) {
try {
if (this._dialog == null) {
this._dialog = true;
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));
let dialog = this._dialogs[l-1]
dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, tlsguard, optstring, sandbox, expanded, expert, action);
if (this._dialogs.length == 1) {
dialog.open();
invocation.return_value(GLib.Variant.new('(b)', [true]));
} else {
invocation.return_value(GLib.Variant.new('(b)', [false]));
}
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() {
log("SGFW: Closed dialog");
this._dialogs.shift();
if (this._dialogs.length > 0) {
log("SGFW: Opening next dialogs (remaining: " + this._dialogs.length + ")");
this._dialogs[0].open();
AddRuleCallback: function(guid, timestamp, rule, scope) {
log("SGFW: Adding rule for " + guid + " " + timestamp + ": " + rule + " (" + scope + ")");
try {
let params = GLib.Variant.new("(usss)", [scope, rule, "*", guid]);
let result = Gio.DBus.system.call_sync("com.subgraph.Firewall",
"/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) {
log("SGFW: Close Async Requested");
this._closeDialogs();
_createDialog: function(guid) {
log("SGFW: Creating new prompt for: " + this._dialogs[guid]);
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) {
log("SGFW: Test Prompt Requested");
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;
}
.fw-prompt-waiting {
font-size: 10pt;
text-align: right;
}
.fw-prompt-icon {
padding: 10px;
}
@ -78,3 +83,33 @@
.fw-details-left {
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"
)
func init() {
sgfw.InitVirtual()
sgfw.InitIPC()
sgfw.InitPrompt()
}
func main() {
sgfw.Main()
}

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

@ -2,7 +2,6 @@ package sgfw
import (
"errors"
"fmt"
"net"
"path"
"strconv"
@ -27,7 +26,7 @@ const introspectXML = `
</method>
<method name="ListRules">
<arg name="rules" direction="out" type="a(ussus)" />
<arg name="rules" direction="out" type="a(usssusssqsqbsuuss)" />
</method>
<method name="DeleteRule">
@ -46,6 +45,28 @@ const introspectXML = `
<arg name="key" direction="in" type="s" />
<arg name="val" direction="in" type="v" />
</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>` +
introspect.IntrospectDataString +
`</node>`
@ -58,15 +79,6 @@ type dbusObjectP struct {
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) {
conn, err := dbus.SystemBus()
if err != nil {
@ -103,7 +115,7 @@ func DbusProcDeathCB(pid int, param interface{}) {
}
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.prompter = newPrompter(conn)
ds.prompter = newPrompter(ds)
pcoroner.AddCallback(DbusProcDeathCB, ds)
return ds, nil
}
@ -168,7 +180,7 @@ func createDbusRule(r *Rule) DbusRule {
pstr += ":" + strconv.Itoa(r.gid)
}
return DbusRule{
return DbusRule {
ID: uint32(r.id),
Net: netstr,
Origin: ostr,
@ -180,7 +192,12 @@ func createDbusRule(r *Rule) DbusRule {
Verb: uint16(r.rtype),
Target: r.AddrString(false),
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 {
r.policy.removeRule(r)
}
if r.mode != RULE_MODE_SESSION {
if r.mode != RULE_MODE_SESSION && r.mode != RULE_MODE_PROCESS {
ds.fw.saveRules()
}
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) {
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()
defer ds.fw.lock.Unlock()
prule := PendingRule{rule: rule, scope: int(scope), policy: policy, guid: guid}
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)
}
@ -335,6 +352,88 @@ func (ds *dbusServer) AddTestVPC(proto string, srcip string, sport uint16, dstip
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 {
log.Debugf("UpdateRule %v", rule)
ds.fw.lock.Lock()
@ -352,20 +451,25 @@ func (ds *dbusServer) UpdateRule(rule DbusRule) *dbus.Error {
return nil
}
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.hostname = tmp.hostname
r.proto = tmp.proto
r.pid = tmp.pid
r.proto = rule.Proto
//r.pid = tmp.pid
r.addr = tmp.addr
r.port = tmp.port
r.uid = int(rule.UID)
r.gid = int(rule.GID)
r.mode = RuleMode(rule.Mode)
r.sandbox = rule.Sandbox
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.dbus.emitRefresh("rules")
} else {
log.Warning("Failed to update rule, rule id `%d` missing.", rule.ID)
}
return nil
}
@ -401,11 +505,12 @@ func (ds *dbusServer) SetConfig(key string, val dbus.Variant) *dbus.Error {
FirewallConfig.DefaultActionID = FilterScope(l)
}
writeConfig()
ds.emitRefresh("config")
return nil
}
func (ob *dbusObjectP) alertRule(data string) {
ob.Call("com.subgraph.fwprompt.EventNotifier.Alert", 0, data)
func (ds *dbusServer) emitRefresh(data string) {
ds.conn.Emit("/com/subgraph/Firewall", "com.subgraph.Firewall.Refresh", data)
}
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)
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
}

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

@ -16,10 +16,11 @@ import (
var gPrompter *prompter = nil
func newPrompter(conn *dbus.Conn) *prompter {
func newPrompter(dbus *dbusServer) *prompter {
p := new(prompter)
p.dbus = dbus
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)
if gPrompter != nil {
@ -32,6 +33,7 @@ func newPrompter(conn *dbus.Conn) *prompter {
}
type prompter struct {
dbus *dbusServer
dbusObj dbus.BusObject
lock sync.Mutex
cond *sync.Cond
@ -265,7 +267,7 @@ func monitorPromptFDLoop() {
}
func init() {
func InitPrompt() {
go monitorPromptFDLoop()
}
@ -388,7 +390,7 @@ func (p *prompter) processConnection(pc pendingConnection) {
policy.fw.saveRules()
}
//log.Warningf("Prompt returning rule: %v", tempRule)
dbusp.alertRule("sgfw prompt added new rule")
p.dbus.emitRefresh("rules")
}
func (p *prompter) nextConnection() (pendingConnection, bool) {
@ -498,7 +500,7 @@ func (p *prompter) nextConnection() (pendingConnection, bool) {
policy.fw.saveRules()
}
//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
}
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)
if len(parts) == 6 && len(strings.TrimSpace(parts[5])) > 0 {
r.saddr = net.ParseIP(parts[5])
if len(parts) == 5 && len(strings.TrimSpace(parts[4])) > 0 {
r.saddr = net.ParseIP(parts[4])
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
}

@ -21,7 +21,6 @@ import (
"github.com/subgraph/go-procsnitch"
)
var dbusp *dbusObjectP = nil
var dbLogger *dbusObjectP = nil
type Firewall struct {
@ -262,17 +261,12 @@ func Main() {
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()
if err != nil {
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)

@ -31,7 +31,7 @@ var tdb *dbusObjectP
var tdbMutex = &sync.Mutex{}
var tdbInit = false
func init() {
func InitVirtual() {
fmt.Println("Initializing virtual packet test subsystem...")
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]
Name=com.subgraph.FirewallPrompt
Exec=/usr/bin/fw-prompt -display :0
Exec=/usr/bin/fw-settings --prompt-only --display :0
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_free

@ -267,6 +267,26 @@ const (
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
const CURRENT_TIME = C.GDK_CURRENT_TIME
@ -385,10 +405,7 @@ func CursorNewFromName(display *Display, name string) (*Cursor, error) {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return &Cursor{obj}, nil
return &Cursor{glib.Take(unsafe.Pointer(c))}, nil
}
// native returns a pointer to the underlying GdkCursor.
@ -446,10 +463,8 @@ func (v *DeviceManager) GetDisplay() (*Display, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return &Display{obj}, nil
return &Display{glib.Take(unsafe.Pointer(c))}, nil
}
/*
@ -497,11 +512,8 @@ func DisplayOpen(displayName string) (*Display, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
d := &Display{obj}
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return d, nil
return &Display{glib.Take(unsafe.Pointer(c))}, nil
}
// DisplayGetDefault() is a wrapper around gdk_display_get_default().
@ -510,11 +522,8 @@ func DisplayGetDefault() (*Display, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
d := &Display{obj}
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return d, nil
return &Display{glib.Take(unsafe.Pointer(c))}, nil
}
// GetName() is a wrapper around gdk_display_get_name().
@ -532,11 +541,8 @@ func (v *Display) GetDefaultScreen() (*Screen, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
s := &Screen{obj}
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return s, nil
return &Screen{glib.Take(unsafe.Pointer(c))}, nil
}
// DeviceIsGrabbed() is a wrapper around gdk_display_device_is_grabbed().
@ -577,6 +583,8 @@ func (v *Display) GetEvent() (*Event, error) {
if c == nil {
return nil, nilPtrErr
}
//The finalizer is not on the glib.Object but on the event.
e := &Event{c}
runtime.SetFinalizer(e, (*Event).free)
return e, nil
@ -588,6 +596,8 @@ func (v *Display) PeekEvent() (*Event, error) {
if c == nil {
return nil, nilPtrErr
}
//The finalizer is not on the glib.Object but on the event.
e := &Event{c}
runtime.SetFinalizer(e, (*Event).free)
return e, nil
@ -645,11 +655,8 @@ func (v *Display) GetDefaultGroup() (*Window, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
w := &Window{obj}
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return w, nil
return &Window{glib.Take(unsafe.Pointer(c))}, nil
}
// SupportsSelectionNotification() is a wrapper around
@ -676,6 +683,7 @@ func (v *Display) SupportsClipboardPersistence() bool {
// TODO(jrick)
func (v *Display) StoreClipboard(clipboardWindow *Window, time uint32, targets ...Atom) {
panic("Not implemented")
}
// SupportsShapes() is a wrapper around gdk_display_supports_shapes().
@ -692,6 +700,7 @@ func (v *Display) SupportsInputShapes() bool {
// TODO(jrick) glib.AppLaunchContext GdkAppLaunchContext
func (v *Display) GetAppLaunchContext() {
panic("Not implemented")
}
// 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.Len = int(length)
sliceHeader.Cap = int(length)
// To make sure the slice doesn't outlive the Pixbuf, add a reference
v.Ref()
runtime.SetFinalizer(&channels, func(_ *[]byte) {
@ -1308,10 +1318,8 @@ func PixbufNew(colorspace Colorspace, hasAlpha bool, bitsPerSample, width, heigh
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
}
// PixbufCopy is a wrapper around gdk_pixbuf_copy().
@ -1320,51 +1328,49 @@ func PixbufCopy(v *Pixbuf) (*Pixbuf, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
}
// PixbufNewFromFile is a wrapper around gdk_pixbuf_new_from_file().
func PixbufNewFromFile(filename string) (*Pixbuf, error) {
cstr := C.CString(filename)
defer C.free(unsafe.Pointer(cstr))
var err *C.GError
res := C.gdk_pixbuf_new_from_file((*C.char)(cstr), &err)
if res == nil {
defer C.g_error_free(err)
return nil, errors.New(C.GoString((*C.char)(err.message)))
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(res))}
p := &Pixbuf{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &Pixbuf{glib.Take(unsafe.Pointer(res))}, nil
}
// PixbufNewFromFileAtSize is a wrapper around gdk_pixbuf_new_from_file_at_size().
func PixbufNewFromFileAtSize(filename string, width, height int) (*Pixbuf, error) {
cstr := C.CString(filename)
defer C.free(unsafe.Pointer(cstr))
var err *C.GError = nil
res := C.gdk_pixbuf_new_from_file_at_size(cstr, C.int(width), C.int(height), &err)
if err != nil {
defer C.g_error_free(err)
return nil, errors.New(C.GoString((*C.char)(err.message)))
}
if res == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(res))}
p := &Pixbuf{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &Pixbuf{glib.Take(unsafe.Pointer(res))}, nil
}
// PixbufNewFromFileAtScale is a wrapper around gdk_pixbuf_new_from_file_at_scale().
func PixbufNewFromFileAtScale(filename string, width, height int, preserveAspectRatio bool) (*Pixbuf, error) {
cstr := C.CString(filename)
defer C.free(unsafe.Pointer(cstr))
var err *C.GError = nil
res := C.gdk_pixbuf_new_from_file_at_scale(cstr, C.int(width), C.int(height),
gbool(preserveAspectRatio), &err)
@ -1372,13 +1378,12 @@ func PixbufNewFromFileAtScale(filename string, width, height int, preserveAspect
defer C.g_error_free(err)
return nil, errors.New(C.GoString((*C.char)(err.message)))
}
if res == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(res))}
p := &Pixbuf{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &Pixbuf{glib.Take(unsafe.Pointer(res))}, nil
}
// 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 {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
}
// RotateSimple is a wrapper around gdk_pixbuf_rotate_simple().
@ -1400,10 +1403,8 @@ func (v *Pixbuf) RotateSimple(angle PixbufRotation) (*Pixbuf, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
}
// ApplyEmbeddedOrientation is a wrapper around gdk_pixbuf_apply_embedded_orientation().
@ -1412,10 +1413,8 @@ func (v *Pixbuf) ApplyEmbeddedOrientation() (*Pixbuf, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
}
// Flip is a wrapper around gdk_pixbuf_flip().
@ -1424,10 +1423,8 @@ func (v *Pixbuf) Flip(horizontal bool) (*Pixbuf, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
}
// 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)
return errors.New(C.GoString((*C.char)(err.message)))
}
return nil
}
@ -1501,10 +1499,7 @@ func PixbufLoaderNew() (*PixbufLoader, error) {
return nil, nilPtrErr
}
//TODO this should be some wrap object
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &PixbufLoader{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
p := &PixbufLoader{glib.Take(unsafe.Pointer(c))}
return p, nil
}
@ -1525,11 +1520,7 @@ func PixbufLoaderNewWithType(t string) (*PixbufLoader, error) {
return nil, nilPtrErr
}
//TODO this should be some wrap object
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &PixbufLoader{obj}
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &PixbufLoader{glib.Take(unsafe.Pointer(c))}, nil
}
// Write() is a wrapper around gdk_pixbuf_loader_write(). The
@ -1582,11 +1573,8 @@ func (v *PixbufLoader) GetPixbuf() (*Pixbuf, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
p := &Pixbuf{obj}
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return p, nil
return &Pixbuf{glib.Take(unsafe.Pointer(c))}, nil
}
type RGBA struct {

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

@ -5,7 +5,6 @@ package gdk
// #include "gdk.go.h"
import "C"
import (
"runtime"
"unsafe"
"github.com/gotk3/gotk3/glib"
@ -54,11 +53,8 @@ func (v *Screen) GetRGBAVisual() (*Visual, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
visual := &Visual{obj}
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return visual, nil
return &Visual{glib.Take(unsafe.Pointer(c))}, nil
}
// GetSystemVisual is a wrapper around gdk_screen_get_system_visual().
@ -67,11 +63,8 @@ func (v *Screen) GetSystemVisual() (*Visual, error) {
if c == nil {
return nil, nilPtrErr
}
obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
visual := &Visual{obj}
obj.Ref()
runtime.SetFinalizer(obj, (*glib.Object).Unref)
return visual, nil
return &Visual{glib.Take(unsafe.Pointer(c))}, nil
}
// 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.
type Application struct {
*Object
ActionMap
}
// native() returns a pointer to the underlying GApplication.
@ -31,7 +33,8 @@ func marshalApplication(p uintptr) (interface{}, error) {
}
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().

@ -146,10 +146,12 @@ type ApplicationFlags int
const (
APPLICATION_FLAGS_NONE ApplicationFlags = C.G_APPLICATION_FLAGS_NONE
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_COMMAND_LINE ApplicationFlags = C.G_APPLICATION_HANDLES_COMMAND_LINE
APPLICATION_SEND_ENVIRONMENT ApplicationFlags = C.G_APPLICATION_SEND_ENVIRONMENT
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.
@ -419,6 +421,22 @@ func (v *Object) native() *C.GObject {
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.
func (v *Object) Native() uintptr {
return uintptr(unsafe.Pointer(v.native()))

@ -68,6 +68,30 @@ toGApplication(void *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 *
toGSettings(void *p)
{
@ -184,12 +208,11 @@ _g_closure_add_finalize_notifier(GClosure *closure)
static inline guint _g_signal_new(const gchar *name) {
return g_signal_new(name,
G_TYPE_OBJECT,
G_SIGNAL_RUN_FIRST,
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
0, NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE,
1,
G_TYPE_POINTER);
0);
}
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_ARRAY = newVariantType(C._G_VARIANT_TYPE_BYTESTRING_ARRAY)
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}
}
// WrapMenuModel exposes wrapMenuModel
func WrapMenuModel(obj *Object) *MenuModel {
return wrapMenuModel(obj)
}
// IsMutable is a wrapper around g_menu_model_is_mutable().
func (v *MenuModel) IsMutable() bool {
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) {
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
}
@ -54,7 +54,7 @@ func AboutDialogNew() (*AboutDialog, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapAboutDialog(obj), nil
}
@ -115,7 +115,7 @@ func (v *AboutDialog) GetLogo() (*gdk.Pixbuf, error) {
return nil, nilPtrErr
}
p := &gdk.Pixbuf{wrapObject(unsafe.Pointer(c))}
p := &gdk.Pixbuf{glib.Take(unsafe.Pointer(c))}
return p, nil
}

@ -88,7 +88,7 @@ func (v *AccelGroup) native() *C.GtkAccelGroup {
func marshalAccelGroup(p uintptr) (interface{}, error) {
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
}
@ -102,7 +102,7 @@ func AccelGroupNew() (*AccelGroup, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapAccelGroup(obj), nil
}
@ -167,7 +167,7 @@ func AccelGroupFromClosure(f interface{}) *AccelGroup {
if c == 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().
@ -214,7 +214,7 @@ func (v *AccelMap) native() *C.GtkAccelMap {
func marshalAccelMap(p uintptr) (interface{}, error) {
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
}
@ -317,7 +317,7 @@ func AccelMapGet() *AccelMap {
if c == 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().
@ -347,7 +347,7 @@ func (v *Menu) GetAccelGroup() *AccelGroup {
if c == 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().

@ -61,7 +61,7 @@ func (v *ActionBar) native() *C.GtkActionBar {
func marshalActionBar(p uintptr) (interface{}, error) {
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 {
@ -74,7 +74,7 @@ func ActionBarNew() (*ActionBar, error) {
if c == nil {
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)
@ -102,5 +102,5 @@ func (a *ActionBar) GetCenterWidget() *Widget {
if w == 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) {
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
}
@ -107,7 +107,7 @@ func (v *AppChooserButton) native() *C.GtkAppChooserButton {
func marshalAppChooserButton(p uintptr) (interface{}, error) {
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 {
@ -124,7 +124,7 @@ func AppChooserButtonNew(content_type string) (*AppChooserButton, error) {
if c == nil {
return nil, nilPtrErr
}
return wrapAppChooserButton(wrapObject(unsafe.Pointer(c))), nil
return wrapAppChooserButton(glib.Take(unsafe.Pointer(c))), nil
}
// TODO: Needs gio/GIcon implemented first
@ -205,7 +205,7 @@ func (v *AppChooserWidget) native() *C.GtkAppChooserWidget {
func marshalAppChooserWidget(p uintptr) (interface{}, error) {
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 {
@ -222,7 +222,7 @@ func AppChooserWidgetNew(content_type string) (*AppChooserWidget, error) {
if c == nil {
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().
@ -318,7 +318,7 @@ func (v *AppChooserDialog) native() *C.GtkAppChooserDialog {
func marshalAppChooserDialog(p uintptr) (interface{}, error) {
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 {
@ -338,7 +338,7 @@ func wrapAppChooserDialog(obj *glib.Object) *AppChooserDialog {
// if c == nil {
// 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().
@ -349,13 +349,13 @@ func AppChooserDialogNewForContentType(parent *Window, flags DialogFlags, conten
if c == nil {
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().
func (v *AppChooserDialog) GetWidget() *AppChooserWidget {
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().

@ -42,12 +42,12 @@ func (v *Application) native() *C.GtkApplication {
func marshalApplication(p uintptr) (interface{}, error) {
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
}
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().
@ -59,7 +59,7 @@ func ApplicationNew(appId string, flags glib.ApplicationFlags) (*Application, er
if c == nil {
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().
@ -78,7 +78,7 @@ func (v *Application) GetWindowByID(id uint) *Window {
if c == 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().
@ -87,7 +87,7 @@ func (v *Application) GetActiveWindow() *Window {
if c == nil {
return nil
}
return wrapWindow(wrapObject(unsafe.Pointer(c)))
return wrapWindow(glib.Take(unsafe.Pointer(c)))
}
// Uninhibit is a wrapper around gtk_application_uninhibit().
@ -101,7 +101,7 @@ func (v *Application) GetAppMenu() *glib.MenuModel {
if c == 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().
@ -116,7 +116,7 @@ func (v *Application) GetMenubar() *glib.MenuModel {
if c == 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().
@ -147,7 +147,7 @@ func (v *Application) GetWindows() *glib.List {
glist := C.gtk_application_get_windows(v.native())
list := glib.WrapList(uintptr(unsafe.Pointer(glist)))
list.DataWrapper(func(ptr unsafe.Pointer) interface{} {
return wrapWindow(wrapObject(ptr))
return wrapWindow(glib.Take(ptr))
})
runtime.SetFinalizer(list, func(l *glib.List) {
l.Free()

@ -45,5 +45,5 @@ func (v *Application) GetMenuByID(id string) *glib.Menu {
if c == 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) {
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
}
@ -46,7 +46,7 @@ func ApplicationWindowNew(app *Application) (*ApplicationWindow, error) {
if c == nil {
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().

@ -50,7 +50,7 @@ func (v *ColorChooser) native() *C.GtkColorChooser {
func marshalColorChooser(p uintptr) (interface{}, error) {
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
}
@ -127,7 +127,7 @@ func (v *ColorChooserDialog) native() *C.GtkColorChooserDialog {
func marshalColorChooserDialog(p uintptr) (interface{}, error) {
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 {
@ -144,5 +144,5 @@ func ColorChooserDialogNew(title string, parent *Window) (*ColorChooserDialog, e
if c == nil {
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) {
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
}
@ -67,7 +67,7 @@ func ComboBoxNew() (*ComboBox, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapComboBox(obj), nil
}
@ -77,7 +77,7 @@ func ComboBoxNewWithEntry() (*ComboBox, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapComboBox(obj), nil
}
@ -87,7 +87,7 @@ func ComboBoxNewWithModel(model ITreeModel) (*ComboBox, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapComboBox(obj), nil
}
@ -141,7 +141,7 @@ func (v *ComboBox) GetModel() (*TreeModel, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapTreeModel(obj), nil
}
@ -150,6 +150,14 @@ func (v *ComboBox) SetModel(model ITreeModel) {
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
*/
@ -170,7 +178,7 @@ func (v *ComboBoxText) native() *C.GtkComboBoxText {
func marshalComboBoxText(p uintptr) (interface{}, error) {
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
}
@ -184,7 +192,7 @@ func ComboBoxTextNew() (*ComboBoxText, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapComboBoxText(obj), nil
}
@ -194,7 +202,7 @@ func ComboBoxTextNewWithEntry() (*ComboBoxText, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapComboBoxText(obj), nil
}

File diff suppressed because it is too large Load Diff

@ -27,7 +27,11 @@ package gtk
// #include <stdlib.h>
// #include <gtk/gtk.h>
import "C"
import "unsafe"
import (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
// ButtonNewFromStock is a wrapper around gtk_button_new_from_stock().
func ButtonNewFromStock(stock Stock) (*Button, error) {
@ -37,7 +41,7 @@ func ButtonNewFromStock(stock Stock) (*Button, error) {
if c == nil {
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().
@ -77,7 +81,7 @@ func ImageNewFromStock(stock Stock, size IconSize) (*Image, error) {
if c == nil {
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().

@ -26,7 +26,11 @@ package gtk
// #cgo pkg-config: gtk+-3.0
// #include <gtk/gtk.h>
import "C"
import "unsafe"
import (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
/*
* GtkDialog
@ -38,7 +42,7 @@ func (v *Dialog) GetActionArea() (*Widget, error) {
if c == nil {
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 {
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().

@ -81,7 +81,7 @@ func AlignmentNew(xalign, yalign, xscale, yscale float32) (*Alignment, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapAlignment(obj), nil
}
@ -107,7 +107,7 @@ func ArrowNew(arrowType ArrowType, shadowType ShadowType) (*Arrow, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapArrow(obj), nil
}
@ -185,7 +185,7 @@ func (v *Arrow) native() *C.GtkArrow {
func marshalArrow(p uintptr) (interface{}, error) {
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
}
@ -213,7 +213,7 @@ func (v *Alignment) native() *C.GtkAlignment {
func marshalAlignment(p uintptr) (interface{}, error) {
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
}
@ -233,7 +233,7 @@ type StatusIcon struct {
func marshalStatusIcon(p uintptr) (interface{}, error) {
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
}
@ -255,7 +255,7 @@ func StatusIconNew() (*StatusIcon, error) {
if c == nil {
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()
@ -266,7 +266,7 @@ func StatusIconNewFromFile(filename string) (*StatusIcon, error) {
if c == nil {
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()
@ -277,7 +277,7 @@ func StatusIconNewFromIconName(iconName string) (*StatusIcon, error) {
if c == nil {
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()
@ -410,7 +410,7 @@ func (v *Misc) native() *C.GtkMisc {
func marshalMisc(p uintptr) (interface{}, error) {
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
}

@ -54,7 +54,7 @@ func goPageSetupDone(setup *C.GtkPageSetup,
delete(pageSetupDoneCallbackRegistry.m, id)
pageSetupDoneCallbackRegistry.Unlock()
obj := wrapObject(unsafe.Pointer(setup))
obj := glib.Take(unsafe.Pointer(setup))
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"
import "C"
import (
"sync"
"unsafe"
"github.com/gotk3/gotk3/gdk"
@ -105,7 +106,7 @@ func ButtonNewFromIconName(iconName string, size IconSize) (*Button, error) {
if c == nil {
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) {
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
}
@ -155,7 +156,7 @@ func HeaderBarNew() (*HeaderBar, error) {
if c == nil {
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().
@ -195,7 +196,7 @@ func (v *HeaderBar) GetCustomTitle() (*Widget, error) {
if c == nil {
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().
@ -254,7 +255,7 @@ func (v *ListBox) native() *C.GtkListBox {
func marshalListBox(p uintptr) (interface{}, error) {
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
}
@ -268,7 +269,7 @@ func ListBoxNew() (*ListBox, error) {
if c == nil {
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().
@ -292,7 +293,7 @@ func (v *ListBox) GetSelectedRow() *ListBoxRow {
if c == 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().
@ -320,7 +321,7 @@ func (v *ListBox) GetActivateOnSingleClick() bool {
// GetAdjustment is a wrapper around gtk_list_box_get_adjustment().
func (v *ListBox) GetAdjustment() *Adjustment {
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}}
}
@ -340,7 +341,7 @@ func (v *ListBox) GetRowAtIndex(index int) *ListBoxRow {
if c == 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().
@ -349,7 +350,7 @@ func (v *ListBox) GetRowAtY(y int) *ListBoxRow {
if c == 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().
@ -367,7 +368,34 @@ func (v *ListBox) InvalidateSort() {
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: SetSortFunc
@ -396,7 +424,7 @@ func (v *ListBoxRow) native() *C.GtkListBoxRow {
func marshalListBoxRow(p uintptr) (interface{}, error) {
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
}
@ -409,7 +437,7 @@ func ListBoxRowNew() (*ListBoxRow, error) {
if c == nil {
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().
@ -423,7 +451,7 @@ func (v *ListBoxRow) GetHeader() *Widget {
if c == 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().
@ -457,7 +485,7 @@ func (v *Revealer) native() *C.GtkRevealer {
func marshalRevealer(p uintptr) (interface{}, error) {
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
}
@ -471,7 +499,7 @@ func RevealerNew() (*Revealer, error) {
if c == nil {
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().
@ -534,7 +562,7 @@ func (v *SearchBar) native() *C.GtkSearchBar {
func marshalSearchBar(p uintptr) (interface{}, error) {
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
}
@ -548,7 +576,7 @@ func SearchBarNew() (*SearchBar, error) {
if c == nil {
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().
@ -604,7 +632,7 @@ func (v *Stack) native() *C.GtkStack {
func marshalStack(p uintptr) (interface{}, error) {
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
}
@ -618,7 +646,7 @@ func StackNew() (*Stack, error) {
if c == nil {
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().
@ -649,7 +677,7 @@ func (v *Stack) GetVisibleChild() *Widget {
if c == 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().

@ -58,4 +58,10 @@ toGtkStackSwitcher(void *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 {
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) {
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
}
@ -71,7 +71,7 @@ func FlowBoxNew() (*FlowBox, error) {
if c == nil {
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()
@ -85,7 +85,7 @@ func (fb *FlowBox) GetChildAtIndex(idx int) *FlowBoxChild {
if c == 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()
@ -176,7 +176,7 @@ func (fb *FlowBox) GetSelectedChildren() (rv []*FlowBoxChild) {
}
list := glib.WrapList(uintptr(unsafe.Pointer(c)))
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)
}
// 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) {
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
}
@ -253,7 +253,7 @@ func FlowBoxChildNew() (*FlowBoxChild, error) {
if c == nil {
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()

@ -10,6 +10,7 @@ package gtk
import "C"
import (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
@ -91,7 +92,7 @@ func (v *StackSidebar) native() *C.GtkStackSidebar {
func marshalStackSidebar(p uintptr) (interface{}, error) {
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
}
@ -105,7 +106,7 @@ func StackSidebarNew() (*StackSidebar, error) {
if c == nil {
return nil, nilPtrErr
}
return wrapStackSidebar(wrapObject(unsafe.Pointer(c))), nil
return wrapStackSidebar(glib.Take(unsafe.Pointer(c))), nil
}
func (v *StackSidebar) SetStack(stack *Stack) {
@ -117,5 +118,11 @@ func (v *StackSidebar) GetStack() *Stack {
if c == 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) {
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 {
@ -47,7 +47,7 @@ func InfoBarNew() (*InfoBar, error) {
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) {
@ -84,7 +84,7 @@ func (v *InfoBar) GetActionArea() (*Widget, error) {
return nil, nilPtrErr
}
return wrapWidget(wrapObject(unsafe.Pointer(c))), nil
return wrapWidget(glib.Take(unsafe.Pointer(c))), nil
}
func (v *InfoBar) GetContentArea() (*Box, error) {
@ -93,7 +93,7 @@ func (v *InfoBar) GetContentArea() (*Box, error) {
return nil, nilPtrErr
}
return wrapBox(wrapObject(unsafe.Pointer(c))), nil
return wrapBox(glib.Take(unsafe.Pointer(c))), nil
}
func (v *InfoBar) GetShowCloseButton() bool {

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

@ -53,7 +53,7 @@ func (v *LevelBar) native() *C.GtkLevelBar {
func marshalLevelBar(p uintptr) (interface{}, error) {
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
}
@ -67,7 +67,7 @@ func LevelBarNew() (*LevelBar, error) {
if c == nil {
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().
@ -76,7 +76,7 @@ func LevelBarNewForInterval(min_value, max_value float64) (*LevelBar, error) {
if c == nil {
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().

@ -32,7 +32,7 @@ func (v *MenuShell) native() *C.GtkMenuShell {
func marshalMenuShell(p uintptr) (interface{}, error) {
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
}

@ -60,7 +60,7 @@ func (v *Popover) native() *C.GtkPopover {
func marshalPopover(p uintptr) (interface{}, error) {
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 {
@ -79,5 +79,5 @@ func PopoverNew(relative IWidget) (*Popover, error) {
if c == nil {
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 {
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
}
/*
* 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
*/
@ -255,7 +243,7 @@ func (ps *PageSetup) native() *C.GtkPageSetup {
func marshalPageSetup(p uintptr) (interface{}, error) {
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
}
@ -269,7 +257,7 @@ func PageSetupNew() (*PageSetup, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapPageSetup(obj), nil
}
@ -279,7 +267,7 @@ func (ps *PageSetup) Copy() (*PageSetup, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapPageSetup(obj), nil
}
@ -390,7 +378,7 @@ func PageSetupNewFromFile(fileName string) (*PageSetup, error) {
defer C.g_error_free(err)
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
}
@ -655,7 +643,7 @@ func (pc *PrintContext) native() *C.GtkPrintContext {
func marshalPrintContext(p uintptr) (interface{}, error) {
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
}
@ -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().
func (pc *PrintContext) GetPageSetup() *PageSetup {
c := C.gtk_print_context_get_page_setup(pc.native())
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapPageSetup(obj)
}
@ -762,7 +750,7 @@ func (v *PrintOperation) toPrintOperationPreview() *C.GtkPrintOperationPreview {
func marshalPrintOperation(p uintptr) (interface{}, error) {
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
}
@ -777,7 +765,7 @@ func PrintOperationNew() (*PrintOperation, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapPrintOperation(obj), nil
}
@ -805,7 +793,7 @@ func (po *PrintOperation) GetDefaultPageSetup() (*PageSetup, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapPageSetup(obj), nil
}
@ -820,7 +808,7 @@ func (po *PrintOperation) GetPrintSettings(ps *PageSetup) (*PrintSettings, error
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapPrintSettings(obj), nil
}
@ -962,7 +950,7 @@ func (po *PrintOperation) GetEmbedPageSetup() bool {
// PrintRunPageSetupDialog() is a wrapper around gtk_print_run_page_setup_dialog().
func PrintRunPageSetupDialog(parent *Window, pageSetup *PageSetup, settings *PrintSettings) *PageSetup {
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)
}
@ -1027,7 +1015,7 @@ func (v *PrintOperationPreview) native() *C.GtkPrintOperationPreview {
func marshalPrintOperationPreview(p uintptr) (interface{}, error) {
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
}
@ -1077,7 +1065,7 @@ func (ps *PrintSettings) native() *C.GtkPrintSettings {
func marshalPrintSettings(p uintptr) (interface{}, error) {
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 {
@ -1125,7 +1113,7 @@ func PrintSettingsNew() (*PrintSettings, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapPrintSettings(obj), nil
}
@ -1135,7 +1123,7 @@ func (ps *PrintSettings) Copy() (*PrintSettings, error) {
if c == nil {
return nil, nilPtrErr
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
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))
}
// 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().
func (ps *PrintSettings) GetPageSet(pages PrintPages) PageSet {
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)
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
}

@ -35,7 +35,7 @@ func (v *Settings) native() *C.GtkSettings {
func marshalSettings(p uintptr) (interface{}, error) {
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 {
@ -49,5 +49,5 @@ func SettingsGetDefault() (*Settings, error) {
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 (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
// 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 {
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) {
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
}
@ -60,7 +60,7 @@ func StackSwitcherNew() (*StackSwitcher, error) {
if c == nil {
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().
@ -74,5 +74,5 @@ func (v *StackSwitcher) GetStack() *Stack {
if c == 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
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapStyleContext(obj), nil
}
@ -107,7 +107,7 @@ func (v *StyleContext) GetScreen() (*gdk.Screen, error) {
return nil, nilPtrErr
}
d := &gdk.Screen{wrapObject(unsafe.Pointer(c))}
d := &gdk.Screen{glib.Take(unsafe.Pointer(c))}
return d, nil
}

@ -6,7 +6,11 @@ package gtk
// #include "gtk.go.h"
import "C"
import "unsafe"
import (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
/*
* GtkTextIter
@ -34,7 +38,7 @@ func (v *TextIter) GetBuffer() *TextBuffer {
if c == 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().

@ -44,7 +44,7 @@ func (v *TextView) native() *C.GtkTextView {
func marshalTextView(p uintptr) (interface{}, error) {
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
}
@ -58,14 +58,14 @@ func TextViewNew() (*TextView, error) {
if c == nil {
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().
func TextViewNewWithBuffer(buf *TextBuffer) (*TextView, error) {
cbuf := buf.native()
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().
@ -74,7 +74,7 @@ func (v *TextView) GetBuffer() (*TextBuffer, error) {
if c == nil {
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().
@ -335,7 +335,7 @@ func (v *TextView) GetWindow(win TextWindowType) *gdk.Window {
if c == 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().

@ -34,7 +34,7 @@ func (v *TreeView) native() *C.GtkTreeView {
func marshalTreeView(p uintptr) (interface{}, error) {
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
}
@ -47,7 +47,7 @@ func setupTreeView(c unsafe.Pointer) (*TreeView, error) {
return nil, nilPtrErr
}
return wrapTreeView(wrapObject(c)), nil
return wrapTreeView(glib.Take(c)), nil
}
// TreeViewNew() is a wrapper around gtk_tree_view_new().
@ -66,7 +66,7 @@ func (v *TreeView) GetModel() (*TreeModel, error) {
if c == nil {
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().
@ -80,7 +80,7 @@ func (v *TreeView) GetSelection() (*TreeSelection, error) {
if c == nil {
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().
@ -192,7 +192,7 @@ func (v *TreeView) GetColumn(n int) *TreeViewColumn {
if c == 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().
@ -211,7 +211,7 @@ func (v *TreeView) GetExpanderColumn() *TreeViewColumn {
if c == 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().
@ -242,7 +242,7 @@ func (v *TreeView) GetCursor() (p *TreePath, c *TreeViewColumn) {
}
if col != nil {
c = wrapTreeViewColumn(wrapObject(unsafe.Pointer(col)))
c = wrapTreeViewColumn(glib.Take(unsafe.Pointer(col)))
}
return
@ -300,7 +300,7 @@ func (v *TreeView) GetBinWindow() *gdk.Window {
return nil
}
w := &gdk.Window{wrapObject(unsafe.Pointer(c))}
w := &gdk.Window{glib.Take(unsafe.Pointer(c))}
return w
}
@ -330,7 +330,7 @@ func (v *TreeView) GetSearchEntry() *Entry {
if c == 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().

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

@ -62,7 +62,7 @@ func (v *Widget) toWidget() *C.GtkWidget {
func marshalWidget(p uintptr) (interface{}, error) {
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
}
@ -336,7 +336,7 @@ func (v *Widget) GetParent() (*Widget, error) {
if c == nil {
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().
@ -364,7 +364,7 @@ func (v *Widget) GetParentWindow() (*gdk.Window, error) {
return nil, nilPtrErr
}
w := &gdk.Window{wrapObject(unsafe.Pointer(c))}
w := &gdk.Window{glib.Take(unsafe.Pointer(c))}
return w, nil
}
@ -456,7 +456,7 @@ func (v *Widget) GetToplevel() (*Widget, error) {
if c == nil {
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().
@ -607,7 +607,7 @@ func (v *Widget) GetWindow() (*gdk.Window, error) {
return nil, nilPtrErr
}
w := &gdk.Window{wrapObject(unsafe.Pointer(c))}
w := &gdk.Window{glib.Take(unsafe.Pointer(c))}
return w, nil
}

@ -48,7 +48,7 @@ func (v *Window) toWindow() *C.GtkWindow {
func marshalWindow(p uintptr) (interface{}, error) {
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
}
@ -62,7 +62,7 @@ func WindowNew(t WindowType) (*Window, error) {
if c == nil {
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().
@ -112,7 +112,7 @@ func (v *Window) GetScreen() (*gdk.Screen, error) {
return nil, nilPtrErr
}
s := &gdk.Screen{wrapObject(unsafe.Pointer(c))}
s := &gdk.Screen{glib.Take(unsafe.Pointer(c))}
return s, nil
}
@ -190,7 +190,7 @@ func (v *Window) GetFocus() (*Widget, error) {
if c == nil {
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().
@ -204,7 +204,7 @@ func (v *Window) GetDefaultWidget() *Widget {
if c == nil {
return nil
}
obj := wrapObject(unsafe.Pointer(c))
obj := glib.Take(unsafe.Pointer(c))
return wrapWidget(obj)
}
@ -283,6 +283,11 @@ func (v *Window) SetDeletable(setting bool) {
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().
func (v *Window) SetSkipTaskbarHint(setting bool) {
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
}
p := &gdk.Pixbuf{wrapObject(unsafe.Pointer(c))}
p := &gdk.Pixbuf{glib.Take(unsafe.Pointer(c))}
return p, nil
}
@ -419,7 +424,7 @@ func (v *Window) GetTransientFor() (*Window, error) {
if c == nil {
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().
@ -428,7 +433,13 @@ func (v *Window) GetAttachedTo() (*Widget, error) {
if c == nil {
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().
@ -557,7 +568,7 @@ func (v *Window) GetApplication() (*Application, error) {
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().
@ -603,7 +614,6 @@ func (v *Window) SetMnemonicModifier(mods gdk.ModifierType) {
// TODO gtk_window_get_default_icon_list().
// TODO gtk_window_get_group().
// TODO gtk_window_get_icon_list().
// TODO gtk_window_get_type_hint().
// TODO gtk_window_get_window_type().
// TODO gtk_window_list_toplevels().
// 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_icon_list().
// TODO gtk_window_set_screen().
// TODO gtk_window_set_type_hint().
// TODO gtk_window_get_resize_grip_area().

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

@ -21,10 +21,9 @@ package pango
// #include <pango/pango.h>
// #include "pango.go.h"
import "C"
import (
// "github.com/andre-hub/gotk3/glib"
// "unsafe"
)
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