func newAudio(Xu *xgbutil.XUtil) (*audio, error) { m, err := OpenMixer() if err != nil { return nil, err } defer func() { // set m to nil on final success if m != nil { _ = m.Close() } }() a := &audio{ mixer: m, } for _, k := range []struct { key string fn keybind.KeyPressFun }{ {"XF86AudioRaiseVolume", a.up}, {"XF86AudioLowerVolume", a.down}, {"XF86AudioMute", a.mute}, } { if err := k.fn.Connect(Xu, Xu.RootWin(), k.key, true); err != nil { return nil, err } } m = nil return a, nil }
func newWindow(X *xgbutil.XUtil) { win, err := xwindow.Generate(X) if err != nil { log.Fatal(err) } win.Create(X.RootWin(), 0, 0, 200, 200, xproto.CwBackPixel|xproto.CwEventMask, 0, xproto.EventMaskButtonRelease) win.WMGracefulClose( func(w *xwindow.Window) { xevent.Detach(w.X, w.Id) mousebind.Detach(w.X, w.Id) w.Destroy() xevent.Quit(X) Done <- true }) win.Map() err = mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { newWindow(X) }).Connect(X, win.Id, "1", false, false) if err != nil { log.Fatal(err) } }
func query(X *xgbutil.XUtil) xinerama.Heads { if X.ExtInitialized("XINERAMA") { heads, err := xinerama.PhysicalHeads(X) if err != nil || len(heads) == 0 { if err == nil { logger.Warning.Printf("Could not find any physical heads " + "with the Xinerama extension.") } else { logger.Warning.Printf("Could not load physical heads via "+ "Xinerama: %s", err) } logger.Warning.Printf("Assuming one head with size equivalent " + "to the root window.") } else { return heads } } // If we're here, then something went wrong or the Xinerama extension // isn't available. So query the root window for its geometry and use that. rgeom := xwindow.RootGeometry(X) return xinerama.Heads{ xrect.New(rgeom.X(), rgeom.Y(), rgeom.Width(), rgeom.Height()), } }
// New allocates and initializes a new DockApp. NewDockApp does not initialize // the window contents and does not map the window to the display screen. The // window is mapped to the screen when the Main method is called on the // returned DockApp. func New(x *xgbutil.XUtil, rect image.Rectangle) (*DockApp, error) { win, err := xwindow.Generate(x) if err != nil { log.Fatalf("generate window: %v", err) } win.Create(x.RootWin(), 0, 0, rect.Size().X, rect.Size().Y, 0) // Set WM hints so that Openbox puts the window into the dock. hints := &icccm.Hints{ Flags: icccm.HintState | icccm.HintIconWindow, InitialState: icccm.StateWithdrawn, IconWindow: win.Id, WindowGroup: win.Id, } err = icccm.WmHintsSet(x, win.Id, hints) if err != nil { win.Destroy() return nil, fmt.Errorf("wm hints: %v", err) } img := xgraphics.New(x, rect) err = img.XSurfaceSet(win.Id) if err != nil { img.Destroy() win.Destroy() return nil, fmt.Errorf("xsurface set: %v", err) } app := &DockApp{ x: x, img: img, win: win, } return app, nil }
func newParent(X *xgbutil.XUtil, cid xproto.Window) (*Parent, error) { parent, err := xwindow.Generate(X) if err != nil { logger.Error.Printf("Could not create a parent window for client "+ "with id '%d' because: %s", cid, err) logger.Error.Fatalf("In a state where no new windows can be created. " + "Unfortunately, we must exit.") } err = parent.CreateChecked(X.RootWin(), 0, 0, 1, 1, xproto.CwEventMask, xproto.EventMaskSubstructureRedirect| xproto.EventMaskButtonPress|xproto.EventMaskButtonRelease) if err != nil { return nil, err } err = xproto.ReparentWindowChecked(X.Conn(), cid, parent.Id, 0, 0).Check() if err != nil { return nil, err } return &Parent{parent}, nil }
// CreateCursorExtra features all available parameters to creating a cursor. // It will return an error if there is a problem with any of the requests // made to create the cursor. // (This implies each request is a checked request. The performance loss is // probably acceptable since cursors should be created once and reused.) func CreateCursorExtra(xu *xgbutil.XUtil, cursor, foreRed, foreGreen, foreBlue, backRed, backGreen, backBlue uint16) (xproto.Cursor, error) { fontId, err := xproto.NewFontId(xu.Conn()) if err != nil { return 0, err } cursorId, err := xproto.NewCursorId(xu.Conn()) if err != nil { return 0, err } err = xproto.OpenFontChecked(xu.Conn(), fontId, uint16(len("cursor")), "cursor").Check() if err != nil { return 0, err } err = xproto.CreateGlyphCursorChecked(xu.Conn(), cursorId, fontId, fontId, cursor, cursor+1, foreRed, foreGreen, foreBlue, backRed, backGreen, backBlue).Check() if err != nil { return 0, err } err = xproto.CloseFontChecked(xu.Conn(), fontId).Check() if err != nil { return 0, err } return cursorId, nil }
// NewDrawable converts an X drawable into a xgraphics.Image. // This is used in NewIcccmIcon. func NewDrawable(X *xgbutil.XUtil, did xproto.Drawable) (*Image, error) { // Get the geometry of the pixmap for use in the GetImage request. pgeom, err := xwindow.RawGeometry(X, xproto.Drawable(did)) if err != nil { return nil, err } // Get the image data for each pixmap. pixmapData, err := xproto.GetImage(X.Conn(), xproto.ImageFormatZPixmap, did, 0, 0, uint16(pgeom.Width()), uint16(pgeom.Height()), (1<<32)-1).Reply() if err != nil { return nil, err } // Now create the xgraphics.Image and populate it with data from // pixmapData and maskData. ximg := New(X, image.Rect(0, 0, pgeom.Width(), pgeom.Height())) // We'll try to be a little flexible with the image format returned, // but not completely flexible. err = readDrawableData(X, ximg, did, pixmapData, pgeom.Width(), pgeom.Height()) if err != nil { return nil, err } return ximg, nil }
// When a cross is declared in its object literal form, it may not have the appropriate window. // this function creates a new X11 window for the cross with the correct geometry depending // on its Icon* parameters. func (c *Cross) CreateWindow(X *xgbutil.XUtil, icons_per_direction int, bg_color uint32) (*xwindow.Window, error) { // calculate the dimensions of the spars of our cross + // width/height reflect the vertical-orientation rectangle width := c.IconMargin*2 + c.IconSize // padding between the icons, margin on the edges height := c.IconSize*icons_per_direction + c.IconPadding*(icons_per_direction-1) + c.IconMargin*2 // intitialize a basic window for the cross win, err := xwindow.Generate(X) if err != nil { return nil, err } win.Create(X.RootWin(), 0, 0, height, height, xproto.CwBackPixel|xproto.CwOverrideRedirect, bg_color, 1) // the rects we will be adding together to form the shape of the cross vert := xrect.New(0, 0, width, height) horiz := xrect.New(0, 0, height, width) struts := []xrect.Rect{vert, horiz} geom, err := win.Geometry() if err != nil { return nil, err } // center struts over window x, y := util.CenterChild(vert, geom) vert.XSet(x) vert.YSet(y) x, y = util.CenterChild(horiz, geom) horiz.XSet(x) horiz.YSet(y) // build the cross shape from our friendly rectangles err = ComposeShape(X, win.Id, struts) if err != nil { return nil, err } // add the window to our cross struct c.Window = win // create icons from our images clr := RGB(bg_color) if c.imagesToBecomeIcons != nil { icons := make(map[string]*Icon, len(c.imagesToBecomeIcons)) for name, img := range c.imagesToBecomeIcons { icon := NewIcon(X, img, win.Id) icon.Background = clr icons[name] = icon } c.Icons = icons } else { return nil, errors.New("Cross: you must create crosses using the NewCross function (this cross has now iconsToBecomeImage)") } return win, nil }
// GrabKeyboard grabs the entire keyboard. // Returns whether GrabStatus is successful and an error if one is reported by // XGB. It is possible to not get an error and the grab to be unsuccessful. // The purpose of 'win' is that after a grab is successful, ALL Key*Events will // be sent to that window. Make sure you have a callback attached :-) func GrabKeyboard(xu *xgbutil.XUtil, win xproto.Window) error { reply, err := xproto.GrabKeyboard(xu.Conn(), false, win, 0, xproto.GrabModeAsync, xproto.GrabModeAsync).Reply() if err != nil { return fmt.Errorf("GrabKeyboard: Error grabbing keyboard on "+ "window '%x': %s", win, err) } switch reply.Status { case xproto.GrabStatusSuccess: // all is well case xproto.GrabStatusAlreadyGrabbed: return fmt.Errorf("GrabKeyboard: Could not grab keyboard. " + "Status: AlreadyGrabbed.") case xproto.GrabStatusInvalidTime: return fmt.Errorf("GrabKeyboard: Could not grab keyboard. " + "Status: InvalidTime.") case xproto.GrabStatusNotViewable: return fmt.Errorf("GrabKeyboard: Could not grab keyboard. " + "Status: NotViewable.") case xproto.GrabStatusFrozen: return fmt.Errorf("GrabKeyboard: Could not grab keyboard. " + "Status: Frozen.") } return nil }
// RootGeometry gets the geometry of the root window. It will panic on failure. func RootGeometry(xu *xgbutil.XUtil) xrect.Rect { geom, err := RawGeometry(xu, xproto.Drawable(xu.RootWin())) if err != nil { panic(err) } return geom }
// Generate is just like New, but generates a new X resource id for you. // Geom is initialized to (0, 0) 1x1. // It is possible for id generation to return an error, in which case, an // error is returned here. func Generate(xu *xgbutil.XUtil) (*Window, error) { wid, err := xproto.NewWindowId(xu.Conn()) if err != nil { return nil, err } return New(xu, wid), nil }
// Ungrab undoes Grab. It will handle all combinations od modifiers found // in xevent.IgnoreMods. func Ungrab(xu *xgbutil.XUtil, win xproto.Window, mods uint16, key xproto.Keycode) { for _, m := range xevent.IgnoreMods { xproto.UngrabKeyChecked(xu.Conn(), key, win, mods|m).Check() } }
func newWindow(X *xgbutil.XUtil, width, height int) *xwindow.Window { var ( err error win *xwindow.Window ) win, err = xwindow.Generate(X) if err != nil { log.Fatal(err) } win.Create(X.RootWin(), 0, 0, width, height, xproto.CwBackPixel|xproto.CwEventMask, 0, xproto.EventMaskButtonRelease) win.WMGracefulClose( func(w *xwindow.Window) { xevent.Detach(w.X, w.Id) mousebind.Detach(w.X, w.Id) w.Destroy() xevent.Quit(X) }) win.Map() if err != nil { log.Fatal(err) } return win }
// Ungrab undoes Grab. It will handle all combinations of modifiers found // in xevent.IgnoreMods. func Ungrab(xu *xgbutil.XUtil, win xproto.Window, mods uint16, button xproto.Button) { for _, m := range xevent.IgnoreMods { xproto.UngrabButtonChecked(xu.Conn(), byte(button), win, mods|m).Check() } }
func newWindow(controlCh *controlCh, X *xgbutil.XUtil, width, height int) *xwindow.Window { var ( err error win *xwindow.Window ) win, err = xwindow.Generate(X) if err != nil { panic(err) } win.Create(X.RootWin(), 0, 0, width, height, xproto.CwBackPixel|xproto.CwEventMask, 0, xproto.EventMaskButtonRelease) // Xorg application exits when the window is closed. win.WMGracefulClose( func(w *xwindow.Window) { xevent.Detach(w.X, w.Id) mousebind.Detach(w.X, w.Id) w.Destroy() xevent.Quit(X) controlCh.exit <- true }) // In order to get ConfigureNotify events, we must listen to the window // using the 'StructureNotify' mask. win.Listen(xproto.EventMaskButtonPress | xproto.EventMaskButtonRelease | xproto.EventMaskKeyPress | xproto.EventMaskKeyRelease | xproto.EventMaskStructureNotify) win.Map() return win }
// find the EWHM window under the mouse cursor func FindManagedWindowUnderMouse(X *xgbutil.XUtil) (xproto.Window, error) { // construct a hashset of the managed windows clients, err := ewmh.ClientListGet(X) if err != nil { return 0, fmt.Errorf("FindUnderMouse: could not retrieve EWHM client list: %v", err) } managed := make(map[xproto.Window]bool, len(clients)) for _, win := range clients { managed[win] = true } cur_window := X.RootWin() // descend the QueryTree to the first child that is a EWMH managed window for { // return the parent if it is an EWHM window if _, ok := managed[cur_window]; ok { return cur_window, nil } cur_window, _, err = FindNextUnderMouse(X, cur_window) if err != nil { break } } // we didn't find the window :( return 0, errors.New("FindUnderMouse: no EWMH window found under mouse") }
func keyHandlers(X *xgbutil.XUtil, cycle *prompt.Cycle, items []*prompt.CycleItem) { geom := headGeom(X) keybind.KeyPressFun( func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) { shown := cycle.Show(geom, cycleNext, items) if !shown { log.Fatal("Did not show cycle prompt.") } cycle.Next() }).Connect(X, X.RootWin(), cycleNext, true) keybind.KeyPressFun( func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) { cycle.Next() }).Connect(X, cycle.GrabId(), cycleNext, true) keybind.KeyPressFun( func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) { shown := cycle.Show(geom, cyclePrev, items) if !shown { log.Fatal("Did not show cycle prompt.") } cycle.Prev() }).Connect(X, X.RootWin(), cyclePrev, true) keybind.KeyPressFun( func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) { cycle.Prev() }).Connect(X, cycle.GrabId(), cyclePrev, true) }
// PhyiscalHeads returns the list of heads in a physical ordering. // Namely, left to right then top to bottom. (Defined by (X, Y).) // Xinerama must have been initialized, otherwise the xinerama.QueryScreens // request will panic. // PhysicalHeads also checks to make sure each rectangle has a unique (x, y) // tuple, so as not to return the geometry of cloned displays. // (At present moment, xgbutil initializes Xinerama automatically during // initial connection.) func PhysicalHeads(xu *xgbutil.XUtil) (Heads, error) { xinfo, err := xinerama.QueryScreens(xu.Conn()).Reply() if err != nil { return nil, err } hds := make(Heads, 0) for _, info := range xinfo.ScreenInfo { head := xrect.New(int(info.XOrg), int(info.YOrg), int(info.Width), int(info.Height)) // Maybe Xinerama is enabled, but we have cloned displays... unique := true for _, h := range hds { if h.X() == head.X() && h.Y() == head.Y() { unique = false break } } if unique { hds = append(hds, head) } } sort.Sort(hds) return hds, nil }
// _NET_DESKTOP_LAYOUT set func DesktopLayoutSet(xu *xgbutil.XUtil, orientation, columns, rows, startingCorner uint) error { return xprop.ChangeProp32(xu, xu.RootWin(), "_NET_DESKTOP_LAYOUT", "CARDINAL", orientation, columns, rows, startingCorner) }
// GetFormat searches SetupInfo for a Format matching the depth provided. func GetFormat(X *xgbutil.XUtil, depth byte) *xproto.Format { for _, pixForm := range X.Setup().PixmapFormats { if pixForm.Depth == depth { return &pixForm } } return nil }
// sendClientMessages is a goroutine that sends client messages to the root // window. We then listen to them later as a demonstration of responding to // X events. (They are sent with SubstructureNotify and SubstructureRedirect // masks set. So in order to receive them, we'll have to explicitly listen // to events of that type on the root window.) func xSource(X *xgbutil.XUtil) { i := 1 for { ewmh.ClientEvent(X, X.RootWin(), "NOOP", i) i++ time.Sleep(200 * time.Millisecond) } }
// RawGeometry isn't smart. It just queries the window given for geometry. func RawGeometry(xu *xgbutil.XUtil, win xproto.Drawable) (xrect.Rect, error) { xgeom, err := xproto.GetGeometry(xu.Conn(), win).Reply() if err != nil { return nil, err } return xrect.New(int(xgeom.X), int(xgeom.Y), int(xgeom.Width), int(xgeom.Height)), nil }
// Grab grabs a key with mods on a particular window. // This will also grab all combinations of modifiers found in xevent.IgnoreMods. func Grab(xu *xgbutil.XUtil, win xproto.Window, mods uint16, key xproto.Keycode) { for _, m := range xevent.IgnoreMods { xproto.GrabKey(xu.Conn(), true, win, mods|m, key, xproto.GrabModeAsync, xproto.GrabModeAsync) } }
func bind(Xu *xgbutil.XUtil, bindings ...binding) error { for _, k := range bindings { if err := k.fn.Connect(Xu, Xu.RootWin(), k.key, true); err != nil { return err } } return nil }
// _NET_DESKTOP_NAMES set func DesktopNamesSet(xu *xgbutil.XUtil, names []string) error { nullterm := make([]byte, 0) for _, name := range names { nullterm = append(nullterm, name...) nullterm = append(nullterm, 0) } return xprop.ChangeProp(xu, xu.RootWin(), 8, "_NET_DESKTOP_NAMES", "UTF8_STRING", nullterm) }
// _NET_SHOWING_DESKTOP req func ShowingDesktopReq(xu *xgbutil.XUtil, show bool) error { var showInt uint if show { showInt = 1 } else { showInt = 0 } return ClientEvent(xu, xu.RootWin(), "_NET_SHOWING_DESKTOP", showInt) }
// _NET_SUPPORTED set // This will create any atoms in the argument if they don't already exist. func SupportedSet(xu *xgbutil.XUtil, atomNames []string) error { atoms, err := xprop.StrToAtoms(xu, atomNames) if err != nil { return err } return xprop.ChangeProp32(xu, xu.RootWin(), "_NET_SUPPORTED", "ATOM", atoms...) }
func clientMessage(X *xgbutil.XUtil, event xgb.Event) { ev := event.(xproto.ClientMessageEvent) cookie := xproto.GetAtomName(X.Conn(), ev.Type) reply, _ := cookie.Reply() log.WithFields(log.Fields{ "name": reply.Name, "data": ev.Data, }).Info("client Message:", ev) }
// _NET_DESKTOP_GEOMETRY get func DesktopGeometryGet(xu *xgbutil.XUtil) (*DesktopGeometry, error) { geom, err := xprop.PropValNums(xprop.GetProperty(xu, xu.RootWin(), "_NET_DESKTOP_GEOMETRY")) if err != nil { return nil, err } return &DesktopGeometry{Width: int(geom[0]), Height: int(geom[1])}, nil }
func NewInput(X *xgbutil.XUtil, theme *InputTheme, config InputConfig) *Input { input := &Input{ X: X, theme: theme, config: config, showing: false, do: nil, history: make([]string, 0, 100), } // Create all windows used for the base of the input prompt. cwin := func(p xproto.Window) *xwindow.Window { return xwindow.Must(xwindow.Create(X, p)) } input.win = cwin(X.RootWin()) input.label = cwin(input.win.Id) input.input = text.NewInput(X, input.win.Id, 1000, theme.Padding, theme.Font, theme.FontSize, theme.FontColor, theme.BgColor) input.bInp = cwin(input.win.Id) input.bTop, input.bBot = cwin(input.win.Id), cwin(input.win.Id) input.bLft, input.bRht = cwin(input.win.Id), cwin(input.win.Id) // Make the top-level window override redirect so the window manager // doesn't mess with us. input.win.Change(xproto.CwOverrideRedirect, 1) input.win.Listen(xproto.EventMaskFocusChange) input.input.Listen(xproto.EventMaskKeyPress) // Colorize the windows. cclr := func(w *xwindow.Window, clr render.Color) { w.Change(xproto.CwBackPixel, clr.Uint32()) } cclr(input.win, input.theme.BgColor) cclr(input.bInp, input.theme.BorderColor) cclr(input.bTop, input.theme.BorderColor) cclr(input.bBot, input.theme.BorderColor) cclr(input.bLft, input.theme.BorderColor) cclr(input.bRht, input.theme.BorderColor) // Map the sub-windows once. input.label.Map() input.input.Map() input.bInp.Map() input.bTop.Map() input.bBot.Map() input.bLft.Map() input.bRht.Map() // Connect the key response handler. // The handler is responsible for tab completion and quitting if the // cancel key has been pressed. input.keyResponse().Connect(X, input.input.Id) input.focusResponse().Connect(X, input.win.Id) return input }