xref: /inferno-os/appl/lib/wmclient.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1*37da2899SCharles.Forsythimplement Wmclient;
2*37da2899SCharles.Forsyth
3*37da2899SCharles.Forsyth#
4*37da2899SCharles.Forsyth# Copyright © 2003 Vita Nuova Holdings Limited
5*37da2899SCharles.Forsyth#
6*37da2899SCharles.Forsyth
7*37da2899SCharles.Forsythinclude "sys.m";
8*37da2899SCharles.Forsyth	sys: Sys;
9*37da2899SCharles.Forsythinclude "draw.m";
10*37da2899SCharles.Forsyth	draw: Draw;
11*37da2899SCharles.Forsyth	Display, Image, Screen, Rect, Point, Pointer, Wmcontext, Context: import draw;
12*37da2899SCharles.Forsythinclude "tk.m";
13*37da2899SCharles.Forsyth	tk: Tk;
14*37da2899SCharles.Forsyth	Toplevel: import tk;
15*37da2899SCharles.Forsythinclude "wmlib.m";
16*37da2899SCharles.Forsyth	wmlib: Wmlib;
17*37da2899SCharles.Forsyth	qword, splitqword, s2r: import wmlib;
18*37da2899SCharles.Forsythinclude "titlebar.m";
19*37da2899SCharles.Forsyth	titlebar: Titlebar;
20*37da2899SCharles.Forsythinclude "wmclient.m";
21*37da2899SCharles.Forsyth
22*37da2899SCharles.ForsythFocusnone, Focusimage, Focustitle: con iota;
23*37da2899SCharles.Forsyth
24*37da2899SCharles.ForsythBdup: con int 16rffffffff;
25*37da2899SCharles.ForsythBddown: con int 16radadadff;
26*37da2899SCharles.Forsyth
27*37da2899SCharles.Forsythinit()
28*37da2899SCharles.Forsyth{
29*37da2899SCharles.Forsyth	sys = load Sys Sys->PATH;
30*37da2899SCharles.Forsyth	draw = load Draw Draw->PATH;
31*37da2899SCharles.Forsyth	tk = load Tk Tk->PATH;
32*37da2899SCharles.Forsyth	wmlib = load Wmlib Wmlib->PATH;
33*37da2899SCharles.Forsyth	if(wmlib == nil){
34*37da2899SCharles.Forsyth		sys->fprint(sys->fildes(2), "wmclient: cannot load %s: %r\n", Wmlib->PATH);
35*37da2899SCharles.Forsyth		raise "fail:bad module";
36*37da2899SCharles.Forsyth	}
37*37da2899SCharles.Forsyth	wmlib->init();
38*37da2899SCharles.Forsyth	titlebar = load Titlebar Titlebar->PATH;
39*37da2899SCharles.Forsyth	if(titlebar == nil){
40*37da2899SCharles.Forsyth		sys->fprint(sys->fildes(2), "wmclient: cannot load %s: %r\n", Titlebar->PATH);
41*37da2899SCharles.Forsyth		raise "fail:bad module";
42*37da2899SCharles.Forsyth	}
43*37da2899SCharles.Forsyth	titlebar->init();
44*37da2899SCharles.Forsyth}
45*37da2899SCharles.Forsyth
46*37da2899SCharles.Forsythmakedrawcontext(): ref Draw->Context
47*37da2899SCharles.Forsyth{
48*37da2899SCharles.Forsyth	return wmlib->makedrawcontext();
49*37da2899SCharles.Forsyth}
50*37da2899SCharles.Forsyth
51*37da2899SCharles.Forsythcursorspec(img: ref Draw->Image): string
52*37da2899SCharles.Forsyth{
53*37da2899SCharles.Forsyth	Hex: con "0123456789abcdef";
54*37da2899SCharles.Forsyth	if(img == nil || img.depth != 1)
55*37da2899SCharles.Forsyth		return "cursor";
56*37da2899SCharles.Forsyth	display := img.display;
57*37da2899SCharles.Forsyth	hot := img.r.min;
58*37da2899SCharles.Forsyth	if(img.r.min.x != 0 || img.r.min.y != 0){
59*37da2899SCharles.Forsyth		n := display.newimage(((0, 0), img.r.size()), Draw->GREY1, 0, Draw->Nofill);
60*37da2899SCharles.Forsyth		n.draw(n.r, img, nil, img.r.min);
61*37da2899SCharles.Forsyth		img = n;
62*37da2899SCharles.Forsyth	}
63*37da2899SCharles.Forsyth	s := sys->sprint("cursor %d %d %d %d ", hot.x, hot.y, img.r.dx(), img.r.dy());
64*37da2899SCharles.Forsyth	nb := img.r.dy() * draw->bytesperline(img.r, img.depth);
65*37da2899SCharles.Forsyth	buf := array[nb] of byte;
66*37da2899SCharles.Forsyth	if(img.readpixels(img.r, buf) == -1)
67*37da2899SCharles.Forsyth		return "cursor";
68*37da2899SCharles.Forsyth
69*37da2899SCharles.Forsyth	for(i := 0; i < nb; i++){
70*37da2899SCharles.Forsyth		c := int buf[i];
71*37da2899SCharles.Forsyth		s[len s] = Hex[c >> 4];
72*37da2899SCharles.Forsyth		s[len s] = Hex[c & 16rf];
73*37da2899SCharles.Forsyth	}
74*37da2899SCharles.Forsyth	return s;
75*37da2899SCharles.Forsyth}
76*37da2899SCharles.Forsyth
77*37da2899SCharles.Forsythblankwin: Window;
78*37da2899SCharles.Forsythwindow(ctxt: ref Draw->Context, title: string, buts: int): ref Window
79*37da2899SCharles.Forsyth{
80*37da2899SCharles.Forsyth	w := ref blankwin;
81*37da2899SCharles.Forsyth	w.ctxt = wmlib->connect(ctxt);
82*37da2899SCharles.Forsyth	w.display = ctxt.display;
83*37da2899SCharles.Forsyth	w.ctl = chan of string;
84*37da2899SCharles.Forsyth	readscreenrect(w);
85*37da2899SCharles.Forsyth
86*37da2899SCharles.Forsyth	if(buts & Plain)
87*37da2899SCharles.Forsyth		return w;
88*37da2899SCharles.Forsyth
89*37da2899SCharles.Forsyth	if(ctxt.wm == nil)
90*37da2899SCharles.Forsyth		buts &= ~(Resize|Hide);
91*37da2899SCharles.Forsyth
92*37da2899SCharles.Forsyth	w.bd = 1;
93*37da2899SCharles.Forsyth	w.titlebar = tk->toplevel(ctxt.display, nil);
94*37da2899SCharles.Forsyth	top := w.titlebar;
95*37da2899SCharles.Forsyth	top.wreq = nil;
96*37da2899SCharles.Forsyth
97*37da2899SCharles.Forsyth	w.ctl = titlebar->new(top, buts);
98*37da2899SCharles.Forsyth	titlebar->settitle(top, title);
99*37da2899SCharles.Forsyth	sizetb(w);
100*37da2899SCharles.Forsyth	w.wmctl("fixedorigin");
101*37da2899SCharles.Forsyth	return w;
102*37da2899SCharles.Forsyth}
103*37da2899SCharles.Forsyth
104*37da2899SCharles.ForsythWindow.pointer(w: self ref Window, p: Draw->Pointer): int
105*37da2899SCharles.Forsyth{
106*37da2899SCharles.Forsyth	if(w.screen == nil || w.titlebar == nil)
107*37da2899SCharles.Forsyth		return 0;
108*37da2899SCharles.Forsyth
109*37da2899SCharles.Forsyth	if(p.buttons && (w.ptrfocus == Focusnone || w.buttons == 0)){
110*37da2899SCharles.Forsyth		if(p.xy.in(w.tbrect))
111*37da2899SCharles.Forsyth			w.ptrfocus = Focustitle;
112*37da2899SCharles.Forsyth		else
113*37da2899SCharles.Forsyth			w.ptrfocus = Focusimage;
114*37da2899SCharles.Forsyth	}
115*37da2899SCharles.Forsyth	w.buttons = p.buttons;
116*37da2899SCharles.Forsyth	if(w.ptrfocus == Focustitle){
117*37da2899SCharles.Forsyth		tk->pointer(w.titlebar, p);
118*37da2899SCharles.Forsyth		return 1;
119*37da2899SCharles.Forsyth	}
120*37da2899SCharles.Forsyth	return 0;
121*37da2899SCharles.Forsyth}
122*37da2899SCharles.Forsyth
123*37da2899SCharles.Forsyth# titlebar requested size might have changed:
124*37da2899SCharles.Forsyth# find out what size it's requesting.
125*37da2899SCharles.Forsythsizetb(w: ref Window)
126*37da2899SCharles.Forsyth{
127*37da2899SCharles.Forsyth	if(w.titlebar == nil)
128*37da2899SCharles.Forsyth		return;
129*37da2899SCharles.Forsyth	w.tbsize = tk->rect(w.titlebar, ".", Tk->Border|Tk->Required).size();
130*37da2899SCharles.Forsyth}
131*37da2899SCharles.Forsyth
132*37da2899SCharles.Forsyth# reshape the image; the space needed for the
133*37da2899SCharles.Forsyth# titlebar is added to r.
134*37da2899SCharles.ForsythWindow.reshape(w: self ref Window, r: Rect)
135*37da2899SCharles.Forsyth{
136*37da2899SCharles.Forsyth	w.r = w.screenr(r);
137*37da2899SCharles.Forsyth	if(w.screen == nil)
138*37da2899SCharles.Forsyth		return;
139*37da2899SCharles.Forsyth	w.wmctl(sys->sprint("!reshape . -1 %s", r2s(w.r)));
140*37da2899SCharles.Forsyth}
141*37da2899SCharles.Forsyth
142*37da2899SCharles.Forsythputimage(w: ref Window, i: ref Image)
143*37da2899SCharles.Forsyth{
144*37da2899SCharles.Forsyth	if(w.screen != nil && i == w.screen.image)
145*37da2899SCharles.Forsyth		return;
146*37da2899SCharles.Forsyth	w.screen = Screen.allocate(i, w.display.color(Draw->White), 0);
147*37da2899SCharles.Forsyth	ir := i.r.inset(w.bd);
148*37da2899SCharles.Forsyth	if(ir.dx() < 0)
149*37da2899SCharles.Forsyth		ir.max.x = ir.min.x;
150*37da2899SCharles.Forsyth	if(ir.dy() < 0)
151*37da2899SCharles.Forsyth		ir.max.y = ir.min.y;
152*37da2899SCharles.Forsyth	if(w.titlebar != nil){
153*37da2899SCharles.Forsyth		w.tbrect = Rect(ir.min, (ir.max.x, ir.min.y + w.tbsize.y));
154*37da2899SCharles.Forsyth		tbimage := w.screen.newwindow(w.tbrect, Draw->Refnone, Draw->Nofill);
155*37da2899SCharles.Forsyth		tk->putimage(w.titlebar, ".", tbimage, nil);
156*37da2899SCharles.Forsyth		ir.min.y = w.tbrect.max.y;
157*37da2899SCharles.Forsyth	}
158*37da2899SCharles.Forsyth	if(ir.dy() < 0)
159*37da2899SCharles.Forsyth		ir.max.y = ir.min.y;
160*37da2899SCharles.Forsyth	w.image = w.screen.newwindow(ir, Draw->Refnone, Draw->Nofill);
161*37da2899SCharles.Forsyth	drawborder(w);
162*37da2899SCharles.Forsyth	w.r = i.r;
163*37da2899SCharles.Forsyth}
164*37da2899SCharles.Forsyth
165*37da2899SCharles.Forsyth# return a rectangle suitable to hold image r when the
166*37da2899SCharles.Forsyth# titlebar and border are included.
167*37da2899SCharles.ForsythWindow.screenr(w: self ref Window, r: Rect): Rect
168*37da2899SCharles.Forsyth{
169*37da2899SCharles.Forsyth	if(w.titlebar != nil){
170*37da2899SCharles.Forsyth		if(r.dx() < w.tbsize.x)
171*37da2899SCharles.Forsyth			r.max.x = r.min.x + w.tbsize.x;
172*37da2899SCharles.Forsyth		r.min.y -= w.tbsize.y;
173*37da2899SCharles.Forsyth	}
174*37da2899SCharles.Forsyth	return r.inset(-w.bd);
175*37da2899SCharles.Forsyth}
176*37da2899SCharles.Forsyth
177*37da2899SCharles.Forsyth# return the available space inside r when space for
178*37da2899SCharles.Forsyth# border and titlebar is taken away.
179*37da2899SCharles.ForsythWindow.imager(w: self ref Window, r: Rect): Rect
180*37da2899SCharles.Forsyth{
181*37da2899SCharles.Forsyth	r = r.inset(w.bd);
182*37da2899SCharles.Forsyth	if(r.dx() < 0)
183*37da2899SCharles.Forsyth		r.max.x = r.min.x;
184*37da2899SCharles.Forsyth	if(r.dy() < 0)
185*37da2899SCharles.Forsyth		r.max.y = r.min.y;
186*37da2899SCharles.Forsyth	if(w.titlebar != nil){
187*37da2899SCharles.Forsyth		r.min.y += w.tbsize.y;
188*37da2899SCharles.Forsyth		if(r.dy() < 0)
189*37da2899SCharles.Forsyth			r.max.y = r.min.y;
190*37da2899SCharles.Forsyth	}
191*37da2899SCharles.Forsyth	return r;
192*37da2899SCharles.Forsyth}
193*37da2899SCharles.Forsyth
194*37da2899SCharles.Forsyth# draw an imitation tk border.
195*37da2899SCharles.Forsythdrawborder(w: ref Window)
196*37da2899SCharles.Forsyth{
197*37da2899SCharles.Forsyth	if(w.screen == nil)
198*37da2899SCharles.Forsyth		return;
199*37da2899SCharles.Forsyth	col := w.display.color(Bdup);
200*37da2899SCharles.Forsyth	i := w.screen.image;
201*37da2899SCharles.Forsyth	r := w.screen.image.r;
202*37da2899SCharles.Forsyth	i.draw((r.min, (r.min.x+w.bd, r.max.y)), col, nil, (0, 0));
203*37da2899SCharles.Forsyth	i.draw(((r.min.x+w.bd, r.min.y), (r.max.x, r.min.y+w.bd)), col, nil, (0, 0));
204*37da2899SCharles.Forsyth	col = w.display.color(Bddown);
205*37da2899SCharles.Forsyth	i.draw(((r.max.x-w.bd, r.min.y+w.bd), r.max), col, nil, (0, 0));
206*37da2899SCharles.Forsyth	i.draw(((r.min.x+w.bd, r.max.y-w.bd), (r.max.x-w.bd, r.max.y)), col, nil, (0, 0));
207*37da2899SCharles.Forsyth}
208*37da2899SCharles.Forsyth
209*37da2899SCharles.Forsythreadscreenrect(w: ref Window)
210*37da2899SCharles.Forsyth{
211*37da2899SCharles.Forsyth	if((fd := sys->open("/chan/wmrect", Sys->OREAD)) != nil){
212*37da2899SCharles.Forsyth		buf := array[12*4] of byte;
213*37da2899SCharles.Forsyth		n := sys->read(fd, buf, len buf);
214*37da2899SCharles.Forsyth		if(n > 0){
215*37da2899SCharles.Forsyth			(w.displayr, nil) = s2r(string buf[0:n], 0);
216*37da2899SCharles.Forsyth			return;
217*37da2899SCharles.Forsyth		}
218*37da2899SCharles.Forsyth	}
219*37da2899SCharles.Forsyth	w.displayr = w.display.image.r;
220*37da2899SCharles.Forsyth}
221*37da2899SCharles.Forsyth
222*37da2899SCharles.ForsythWindow.onscreen(w: self ref Window, how: string)
223*37da2899SCharles.Forsyth{
224*37da2899SCharles.Forsyth	if(how == nil)
225*37da2899SCharles.Forsyth		how = "place";
226*37da2899SCharles.Forsyth	w.wmctl(sys->sprint("!reshape . -1 %s %q", r2s(w.r), how));
227*37da2899SCharles.Forsyth}
228*37da2899SCharles.Forsyth
229*37da2899SCharles.ForsythWindow.startinput(w: self ref Window, devs: list of string)
230*37da2899SCharles.Forsyth{
231*37da2899SCharles.Forsyth	for(; devs != nil; devs = tl devs)
232*37da2899SCharles.Forsyth		w.wmctl(sys->sprint("start %q", hd devs));
233*37da2899SCharles.Forsyth}
234*37da2899SCharles.Forsyth
235*37da2899SCharles.Forsyth# commands originating both from tkclient and wm (via ctl)
236*37da2899SCharles.ForsythWindow.wmctl(w: self ref Window, req: string): string
237*37da2899SCharles.Forsyth{
238*37da2899SCharles.Forsyth	(c, next) := qword(req, 0);
239*37da2899SCharles.Forsyth	case c {
240*37da2899SCharles.Forsyth	"exit" =>
241*37da2899SCharles.Forsyth		sys->fprint(sys->open("/prog/" + string sys->pctl(0, nil) + "/ctl", Sys->OWRITE), "killgrp");
242*37da2899SCharles.Forsyth		exit;
243*37da2899SCharles.Forsyth	# old-style requests: pass them back around in proper form.
244*37da2899SCharles.Forsyth	"move" =>
245*37da2899SCharles.Forsyth		# move x y
246*37da2899SCharles.Forsyth		if(w.titlebar != nil)
247*37da2899SCharles.Forsyth			titlebar->sendctl(w.titlebar, "!move . -1 " + req[next:]);
248*37da2899SCharles.Forsyth	"size" =>
249*37da2899SCharles.Forsyth		if(w.titlebar != nil){
250*37da2899SCharles.Forsyth			minsz := titlebar->minsize(w.titlebar);
251*37da2899SCharles.Forsyth			titlebar->sendctl(w.titlebar, "!size . -1 " + string minsz.x + " " + string minsz.y);
252*37da2899SCharles.Forsyth		}
253*37da2899SCharles.Forsyth	"ok" or
254*37da2899SCharles.Forsyth	"help" =>
255*37da2899SCharles.Forsyth		;
256*37da2899SCharles.Forsyth	"rect" =>
257*37da2899SCharles.Forsyth		(w.displayr, nil) = s2r(req, next);
258*37da2899SCharles.Forsyth	"haskbdfocus" =>
259*37da2899SCharles.Forsyth		w.focused = int qword(req, next).t0;
260*37da2899SCharles.Forsyth		if(w.titlebar != nil){
261*37da2899SCharles.Forsyth			tk->cmd(w.titlebar, "focus -global " + string w.focused);
262*37da2899SCharles.Forsyth			tk->cmd(w.titlebar, "update");
263*37da2899SCharles.Forsyth		}
264*37da2899SCharles.Forsyth		drawborder(w);
265*37da2899SCharles.Forsyth	"task" =>
266*37da2899SCharles.Forsyth		title := "";
267*37da2899SCharles.Forsyth		if(w.titlebar != nil)
268*37da2899SCharles.Forsyth			title = titlebar->title(w.titlebar);
269*37da2899SCharles.Forsyth		wmreq(w, sys->sprint("task %q", title), next);
270*37da2899SCharles.Forsyth		w.saved = w.r.min;
271*37da2899SCharles.Forsyth		# send window out of the way
272*37da2899SCharles.Forsyth		# XXX oops, can't do this for plain windows...
273*37da2899SCharles.Forsyth		titlebar->sendctl(w.titlebar, "!reshape . -1 " + r2s((w.displayr.max, w.displayr.max.add(w.r.size()))));
274*37da2899SCharles.Forsyth	"untask" =>
275*37da2899SCharles.Forsyth		wmreq(w, req, next);
276*37da2899SCharles.Forsyth		# put window back where it was before.
277*37da2899SCharles.Forsyth		# XXX what do we we do if the window manager window has been reshape in the meantime...?
278*37da2899SCharles.Forsyth		titlebar->sendctl(w.titlebar, "!reshape . -1 " + r2s((w.saved, w.saved.add(w.r.size()))));
279*37da2899SCharles.Forsyth	* =>
280*37da2899SCharles.Forsyth		return wmreq(w, req, next);
281*37da2899SCharles.Forsyth	}
282*37da2899SCharles.Forsyth	return nil;
283*37da2899SCharles.Forsyth}
284*37da2899SCharles.Forsyth
285*37da2899SCharles.Forsythwmreq(w: ref Window, req: string, e: int): string
286*37da2899SCharles.Forsyth{
287*37da2899SCharles.Forsyth	name: string;
288*37da2899SCharles.Forsyth	if(req != nil && req[0] == '!'){
289*37da2899SCharles.Forsyth		(name, e) = qword(req, e);
290*37da2899SCharles.Forsyth		if(name != ".")
291*37da2899SCharles.Forsyth			return "invalid window name";
292*37da2899SCharles.Forsyth	}
293*37da2899SCharles.Forsyth	if(w.ctxt.connfd != nil){
294*37da2899SCharles.Forsyth		if(sys->fprint(w.ctxt.connfd, "%s", req) == -1)
295*37da2899SCharles.Forsyth			return sys->sprint("%r");
296*37da2899SCharles.Forsyth		if(req[0] == '!')
297*37da2899SCharles.Forsyth			recvimage(w);
298*37da2899SCharles.Forsyth		return nil;
299*37da2899SCharles.Forsyth	}
300*37da2899SCharles.Forsyth	# if we're getting an image and there's no window manager,
301*37da2899SCharles.Forsyth	# then there's only one image to get...
302*37da2899SCharles.Forsyth	if(req[0] == '!')
303*37da2899SCharles.Forsyth		putimage(w, w.ctxt.ctxt.display.image);
304*37da2899SCharles.Forsyth	else{
305*37da2899SCharles.Forsyth		(nil, nil, err) := wmlib->wmctl(w.ctxt, req);
306*37da2899SCharles.Forsyth		return err;
307*37da2899SCharles.Forsyth	}
308*37da2899SCharles.Forsyth	return nil;
309*37da2899SCharles.Forsyth}
310*37da2899SCharles.Forsyth
311*37da2899SCharles.Forsythrecvimage(w: ref Window)
312*37da2899SCharles.Forsyth{
313*37da2899SCharles.Forsyth	i := <-w.ctxt.images;
314*37da2899SCharles.Forsyth	if(i == nil)
315*37da2899SCharles.Forsyth		i = <-w.ctxt.images;
316*37da2899SCharles.Forsyth	putimage(w, i);
317*37da2899SCharles.Forsyth}
318*37da2899SCharles.Forsyth
319*37da2899SCharles.ForsythWindow.settitle(w: self ref Window, title: string): string
320*37da2899SCharles.Forsyth{
321*37da2899SCharles.Forsyth	if(w.titlebar == nil)
322*37da2899SCharles.Forsyth		return nil;
323*37da2899SCharles.Forsyth	oldr := w.imager(w.r);
324*37da2899SCharles.Forsyth	old := titlebar->settitle(w.titlebar, title);
325*37da2899SCharles.Forsyth	sizetb(w);
326*37da2899SCharles.Forsyth	if(w.tbsize.x < w.r.dx())
327*37da2899SCharles.Forsyth		tk->putimage(w.titlebar, ".", w.titlebar.image, nil);	# unsuspend the window
328*37da2899SCharles.Forsyth	else
329*37da2899SCharles.Forsyth		w.wmctl("!reshape . -1 " + r2s(w.screenr(oldr)));
330*37da2899SCharles.Forsyth	return old;
331*37da2899SCharles.Forsyth}
332*37da2899SCharles.Forsyth
333*37da2899SCharles.Forsythsnarfget(): string
334*37da2899SCharles.Forsyth{
335*37da2899SCharles.Forsyth	return wmlib->snarfget();
336*37da2899SCharles.Forsyth}
337*37da2899SCharles.Forsyth
338*37da2899SCharles.Forsythsnarfput(buf: string)
339*37da2899SCharles.Forsyth{
340*37da2899SCharles.Forsyth	return wmlib->snarfput(buf);
341*37da2899SCharles.Forsyth}
342*37da2899SCharles.Forsyth
343*37da2899SCharles.Forsythr2s(r: Rect): string
344*37da2899SCharles.Forsyth{
345*37da2899SCharles.Forsyth	return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
346*37da2899SCharles.Forsyth}
347