client.go RawHistoryFolder

1package core
2
3import (
4	"context"
5
6	"github.com/bnema/neferwl/internal/ports"
7)
8
9// clientEvent applies one client event. publish asks for a new scene;
10// stop ends Run (a command could not be delivered).
11func (c *Core) clientEvent(ctx context.Context, ev ports.ClientEvent) (publish, stop bool) {
12	switch v := ev.(type) {
13	case ports.SessionLockChanged:
14		if !c.applyLockChanged(v) {
15			return false, false
16		}
17	case ports.CaptureSessionOpen:
18		c.captureOpen(v)
19	case ports.CaptureSessionClose:
20		c.captureClose(v.ID)
21	case ports.CaptureFrameTaken:
22		// Nothing new to show (the target flashes already): no scene.
23		if !c.captureFrame(v) {
24			return false, false
25		}
26	case ports.CaptureExclusionBegin:
27		c.captureExclusionBegin(v)
28	case ports.CaptureExclusionLayer:
29		c.captureExclusionLayer(v)
30	case ports.CaptureExclusionEnd:
31		c.captureExclusionEnd(v.Session)
32	case ports.LayerChanged:
33		c.layerChanged = true
34		c.setLayers(v.Layers)
35	case ports.InputRegionChanged:
36		c.windows.setRegion(v)
37	case ports.WindowMapped:
38		c.mapWindow(v)
39	case ports.WindowResized:
40		c.resizeFloating(v)
41	case ports.PopupRequest:
42		if err := c.placePopup(ctx, v); err != nil {
43			return false, true
44		}
45	case ports.PopupMapped:
46		if p := c.popups[v.ID]; p != nil {
47			p.mapped = true
48		}
49	case ports.ShortcutsInhibit:
50		c.windows.setInhibitShortcuts(v.Window, v.Active)
51		if err := c.updateInhibit(ctx); err != nil {
52			return false, true
53		}
54		return false, false
55	case ports.OutputPower:
56		if i := c.screenIndex(v.Output); i >= 0 && c.screens[i].off == v.On {
57			c.screens[i].off = !v.On
58		}
59	case ports.IdleInhibit:
60		c.windows.setIdleInhibit(v.Window, v.Active)
61		c.publishState()
62		return false, false
63	case ports.WindowAppID:
64		c.windows.setAppID(v.ID, v.AppID)
65	case ports.WindowParent:
66		if _, w := c.screenOf(v.ID); w != nil {
67			w.SetDialogParent(v.ID, v.Parent)
68		}
69	case ports.WindowModal:
70		if _, w := c.screenOf(v.ID); w != nil {
71			w.SetModal(v.ID, v.Modal)
72		}
73	case ports.UnmanagedMapped, ports.UnmanagedMoved, ports.UnmanagedUnmapped:
74		changed, err := c.unmanagedEvent(ctx, v)
75		if err != nil {
76			return false, true
77		}
78		if !changed {
79			return false, false
80		}
81	case ports.WindowUnmapped:
82		if c.unmapClient(ctx, v) != nil {
83			return false, true
84		}
85	case ports.PointerConstrained:
86		c.constrained = v
87	case ports.PointerWarp:
88		if c.warpPointer(ctx, v) != nil {
89			return false, true
90		}
91	case ports.WindowMoveRequest:
92		if c.clientDrag(ctx, v) != nil {
93			return false, true
94		}
95	case ports.DataDragStarted:
96		if c.startDataDrag(ctx, v) != nil {
97			return false, true
98		}
99		return false, false
100	case ports.DataDragIcon:
101		if !c.setDataDragIcon(v) {
102			return false, false
103		}
104	case ports.DataDragEnded:
105		if c.dataDrag == nil {
106			return false, false
107		}
108		c.abortDataDrag()
109		if c.finishDataDrag(ctx) != nil {
110			return false, true
111		}
112		return false, false
113	case ports.WindowFullscreenRequest:
114		if c.fullscreenRequest(ctx, v) != nil {
115			return false, true
116		}
117	case ports.WorkspaceActivate:
118		if c.activateWorkspaces(ctx, v) != nil {
119			return false, true
120		}
121	case ports.WindowActivate:
122		if c.activate(ctx, v.ID) != nil {
123			return false, true
124		}
125	}
126	return true, false
127}
128
129// unmapClient drops a window or popup the client unmapped.
130func (c *Core) unmapClient(ctx context.Context, v ports.WindowUnmapped) error {
131	if c.popups[v.ID] != nil {
132		if err := c.dropPopup(ctx, v.ID); err != nil {
133			return err
134		}
135	}
136	if err := c.closePopupsOf(ctx, v.ID); err != nil {
137		return err
138	}
139	c.windows.drop(v.ID)
140	if c.drag != nil && c.drag.id == v.ID {
141		c.abortDrag()
142	}
143	if err := c.dataDragGone(ctx, v.ID); err != nil {
144		return err
145	}
146	c.unmapWindow(v)
147	if c.pgrab != nil && !c.pgrab.valid() {
148		if err := c.abortPointerGrab(ctx); err != nil {
149			return err
150		}
151	}
152	c.releaseSlots()
153	if c.pointer == v.ID {
154		c.pointer = 0
155		if err := c.pointerFocus(ctx, ports.PointerFocus{}); err != nil {
156			return err
157		}
158	}
159	return nil
160}
161
162// fullscreenRequest answers xdg_toplevel set/unset_fullscreen, from the
163// window itself or a taskbar (External).
164func (c *Core) fullscreenRequest(ctx context.Context, v ports.WindowFullscreenRequest) error {
165	c.configures.answer[v.ID] = true
166	if v.Fullscreen && !v.External && c.now().Sub(c.windows.lookup(v.ID).mappedAt) < fullscreenGrace {
167		return nil
168	}
169	// A taskbar request is a user action on that window, as an
170	// activation: it comes on screen with the focus, leaving
171	// another window's fullscreen.
172	if v.External && v.Fullscreen {
173		if err := c.activate(ctx, v.ID); err != nil {
174			return err
175		}
176	}
177	if s, _ := c.screenOf(v.ID); s != nil {
178		s.mon.SetFullscreen(v.ID, v.Fullscreen)
179		if w := s.mon.Current(); v.Fullscreen && w.fullscreen == v.ID {
180			// A taskbar or late fullscreen request that took
181			// effect: the window does not fade in, it keeps the
182			// direct scanout path. One that was ignored (the
183			// grace after the map) leaves the entrance running.
184			delete(s.rects, v.ID)
185		}
186	}
187	return nil
188}
189
190// activateWorkspaces shows the workspaces of v by stable ID, in order.
191func (c *Core) activateWorkspaces(ctx context.Context, v ports.WorkspaceActivate) error {
192	before := c.cur().mon.Current()
193	for _, id := range v.IDs {
194		for i, sc := range c.screens {
195			if sc.name() == "" {
196				continue
197			}
198			for w := range sc.mon.all() {
199				if w.ID == id {
200					c.focusScreen = i
201					sc.mon.show(w)
202					break
203				}
204			}
205		}
206	}
207	return c.workspaceVisible(ctx, c.cur().mon.Current() != before)
208}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 23

Tags 10

↑↓ moveenter openesc close