Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement focus events #599

Merged
merged 4 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion _demos/mouse.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,14 @@ func main() {
s.SetStyle(defStyle)
s.EnableMouse()
s.EnablePaste()
s.EnableFocus()
s.Clear()

posfmt := "Mouse: %d, %d "
btnfmt := "Buttons: %s"
keyfmt := "Keys: %s"
pastefmt := "Paste: [%d] %s"
focusfmt := "Focus: %s"
stefanhaller marked this conversation as resolved.
Show resolved Hide resolved
white := tcell.StyleDefault.
Foreground(tcell.ColorMidnightBlue).Background(tcell.ColorLightCoral)

Expand All @@ -148,9 +150,10 @@ func main() {
pstr := ""
ecnt := 0
pasting := false
focus := true // assume we are focused when we start

for {
drawBox(s, 1, 1, 42, 7, white, ' ')
drawBox(s, 1, 1, 42, 8, white, ' ')
emitStr(s, 2, 2, white, "Press ESC twice to exit, C to clear.")
emitStr(s, 2, 3, white, fmt.Sprintf(posfmt, mx, my))
emitStr(s, 2, 4, white, fmt.Sprintf(btnfmt, bstr))
Expand All @@ -162,6 +165,12 @@ func main() {
}
emitStr(s, 2, 6, white, fmt.Sprintf(pastefmt, len(pstr), ps))

fstr := "false"
if focus {
fstr = "true"
}
emitStr(s, 2, 7, white, fmt.Sprintf(focusfmt, fstr))

s.Show()
bstr = ""
ev := s.PollEvent()
Expand Down Expand Up @@ -315,6 +324,8 @@ func main() {
lchar = ch
s.SetContent(w-1, h-1, 'M', nil, st)
mx, my = x, y
case *tcell.EventFocus:
focus = ev.Focused
default:
s.SetContent(w-1, h-1, 'X', nil, st)
}
Expand Down
4 changes: 4 additions & 0 deletions console_win.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ func (s *cScreen) EnablePaste() {}

func (s *cScreen) DisablePaste() {}

func (s *cScreen) EnableFocus() {}

func (s *cScreen) DisableFocus() {}

func (s *cScreen) Fini() {
s.disengage()
}
Expand Down
28 changes: 28 additions & 0 deletions focus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2023 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tcell

// EventFocus is a focus event. It is sent when the terminal window (or tab)
// gets or loses focus.
type EventFocus struct {
*EventTime

// True if the window received focus, false if it lost focus
Focused bool
}

func NewEventFocus(focused bool) *EventFocus {
return &EventFocus{Focused: focused}
}
6 changes: 6 additions & 0 deletions screen.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ type Screen interface {
// DisablePaste disables bracketed paste mode.
DisablePaste()

// EnableFocus enables reporting of focus events, if your terminal supports it.
EnableFocus()

// DisableFocus disables reporting of focus events.
DisableFocus()

// HasMouse returns true if the terminal (apparently) supports a
// mouse. Note that the return value of true doesn't guarantee that
// a mouse/pointing device is present; a false return definitely
Expand Down
6 changes: 6 additions & 0 deletions simulation.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,12 @@ func (s *simscreen) DisablePaste() {
s.paste = false
}

func (s *simscreen) EnableFocus() {
}

func (s *simscreen) DisableFocus() {
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't bother setting a flag on s here like it's done for mouse and paste; as far as I can tell, s.mouse and s.paste are not used anywhere, so I don't understand why they exist. Let me know if I'm missing something, or whether you'd like me to maintain an unused s.focus flag for consistency.

func (s *simscreen) Size() (int, int) {
s.Lock()
w, h := s.back.Size()
Expand Down
2 changes: 2 additions & 0 deletions terminfo/terminfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ type Terminfo struct {
EnterUrl string
ExitUrl string
SetWindowSize string
EnableFocusReporting string
DisableFocusReporting string
}

const (
Expand Down
79 changes: 79 additions & 0 deletions tscreen.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ type tScreen struct {
enterUrl string
exitUrl string
setWinSize string
enableFocus string
disableFocus string
cursorStyles map[CursorStyle]string
cursorStyle CursorStyle
saved *term.State
Expand All @@ -161,6 +163,7 @@ type tScreen struct {
wg sync.WaitGroup
mouseFlags MouseFlags
pasteEnabled bool
focusEnabled bool

sync.Mutex
}
Expand Down Expand Up @@ -366,6 +369,17 @@ func (t *tScreen) prepareExtendedOSC() {
} else if t.ti.Mouse != "" {
t.setWinSize = "\x1b[8;%p1%p2%d;%dt"
}

if t.ti.EnableFocusReporting != "" {
t.enableFocus = t.ti.EnableFocusReporting
} else if t.ti.Mouse != "" {
t.enableFocus = "\x1b[?1004h"
}
if t.ti.DisableFocusReporting != "" {
t.disableFocus = t.ti.DisableFocusReporting
} else if t.ti.Mouse != "" {
t.disableFocus = "\x1b[?1004l"
}
}

func (t *tScreen) prepareCursorStyles() {
Expand Down Expand Up @@ -1043,6 +1057,32 @@ func (t *tScreen) enablePasting(on bool) {
}
}

func (t *tScreen) EnableFocus() {
t.Lock()
t.focusEnabled = true
t.enableFocusReporting()
t.Unlock()
}

func (t *tScreen) DisableFocus() {
t.Lock()
t.focusEnabled = false
t.disableFocusReporting()
t.Unlock()
}

func (t *tScreen) enableFocusReporting() {
if t.enableFocus != "" {
t.TPuts(t.enableFocus)
}
}

func (t *tScreen) disableFocusReporting() {
if t.disableFocus != "" {
t.TPuts(t.disableFocus)
}
}

func (t *tScreen) Size() (int, int) {
t.Lock()
w, h := t.w, t.h
Expand Down Expand Up @@ -1380,6 +1420,35 @@ func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
return true, false
}

func (t *tScreen) parseFocus(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
state := 0
b := buf.Bytes()
for i := range b {
switch state {
case 0:
if b[i] != '\x1b' {
return false, false
}
state = 1
case 1:
if b[i] != '[' {
return false, false
}
state = 2
case 2:
if b[i] != 'I' && b[i] != 'O' {
return false, false
}
*evs = append(*evs, NewEventFocus(b[i] == 'I'))
stefanhaller marked this conversation as resolved.
Show resolved Hide resolved
_, _ = buf.ReadByte()
_, _ = buf.ReadByte()
_, _ = buf.ReadByte()
return true, true
}
}
return true, false
}

// parseXtermMouse is like parseSgrMouse, but it parses a legacy
// X11 mouse record.
func (t *tScreen) parseXtermMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
Expand Down Expand Up @@ -1556,6 +1625,12 @@ func (t *tScreen) collectEventsFromInput(buf *bytes.Buffer, expire bool) []Event
partials++
}

if part, comp := t.parseFocus(buf, &res); comp {
continue
} else if part {
partials++
}

// Only parse mouse records if this term claims to have
// mouse support

Expand Down Expand Up @@ -1804,6 +1879,9 @@ func (t *tScreen) engage() error {
t.stopQ = stopQ
t.enableMouse(t.mouseFlags)
t.enablePasting(t.pasteEnabled)
if t.focusEnabled {
t.enableFocusReporting()
}

ti := t.ti
t.TPuts(ti.EnterCA)
Expand Down Expand Up @@ -1853,6 +1931,7 @@ func (t *tScreen) disengage() {
t.TPuts(ti.ExitKeypad)
t.enableMouse(0)
t.enablePasting(false)
t.disableFocusReporting()

_ = t.tty.Stop()
}
Expand Down