xref: /inferno-os/appl/lib/tkclient.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Tkclient;
2
3#
4# Copyright © 2003 Vita Nuova Holdings Limited
5#
6
7include "sys.m";
8	sys: Sys;
9include "draw.m";
10	draw: Draw;
11	Display, Image, Screen, Rect, Point, Pointer, Wmcontext, Context: import draw;
12include "tk.m";
13	tk: Tk;
14	Toplevel: import tk;
15include "wmlib.m";
16	wmlib: Wmlib;
17	qword, splitqword, s2r: import wmlib;
18include "titlebar.m";
19	titlebar: Titlebar;
20include "tkclient.m";
21
22Background: con int 16r777777FF;		# should be drawn over immediately, but just in case...
23
24init()
25{
26	sys = load Sys Sys->PATH;
27	draw = load Draw Draw->PATH;
28	tk = load Tk Tk->PATH;
29	wmlib = load Wmlib Wmlib->PATH;
30	if(wmlib == nil){
31		sys->fprint(sys->fildes(2), "tkclient: cannot load %s: %r\n", Wmlib->PATH);
32		raise "fail:bad module";
33	}
34	wmlib->init();
35	titlebar = load Titlebar Titlebar->PATH;
36	if(titlebar == nil){
37		sys->fprint(sys->fildes(2), "tkclient: cannot load %s: %r\n", Titlebar->PATH);
38		raise "fail:bad module";
39	}
40	titlebar->init();
41}
42
43makedrawcontext(): ref Draw->Context
44{
45	return wmlib->makedrawcontext();
46}
47
48toplevel(ctxt: ref Draw->Context, topconfig: string, title: string, buts: int): (ref Tk->Toplevel, chan of string)
49{
50	wm := wmlib->connect(ctxt);
51	opts := "";
52	if((buts & Plain) == 0)
53		opts = "-borderwidth 1 -relief raised ";
54	top := tk->toplevel(wm.ctxt.display, opts+topconfig);
55	if (top == nil) {
56		sys->fprint(sys->fildes(2), "wmlib: window creation failed (top %ux, i %ux)\n", top, top.image);
57		raise "fail:window creation failed";
58	}
59	top.ctxt = wm;
60	readscreenrect(top);
61	c := titlebar->new(top, buts);
62	titlebar->settitle(top, title);
63	return (top, c);
64}
65
66readscreenrect(top: ref Tk->Toplevel)
67{
68	if((fd := sys->open("/chan/wmrect", Sys->OREAD)) != nil){
69		buf := array[12*4] of byte;
70		n := sys->read(fd, buf, len buf);
71		if(n > 0)
72			(top.screenr, nil) = s2r(string buf[0:n], 0);
73	}
74}
75
76onscreen(top: ref Tk->Toplevel, how: string)
77{
78	if(how == nil)
79		how = "place";
80	wmctl(top, sys->sprint("!reshape . -1 %s %q",
81			r2s(tk->rect(top, ".", Tk->Border|Tk->Required)), how));
82}
83
84startinput(top: ref Tk->Toplevel, devs: list of string)
85{
86	for(; devs != nil; devs = tl devs)
87		wmctl(top, sys->sprint("start %q", hd devs));
88}
89
90r2s(r: Rect): string
91{
92	return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
93}
94
95# commands originating both from tkclient and wm (via ctl)
96wmctl(top: ref Tk->Toplevel, req: string): string
97{
98#sys->print("wmctl %s\n", req);
99	(c, next) := qword(req, 0);
100	case c {
101	"exit" =>
102		sys->fprint(sys->open("/prog/" + string sys->pctl(0, nil) + "/ctl", Sys->OWRITE), "killgrp");
103		exit;
104	# old-style requests: pass them back around in proper form.
105	"move" =>
106		# move x y
107		titlebar->sendctl(top, "!move . -1 " + req[next:]);
108	"size" =>
109		minsz := titlebar->minsize(top);
110		titlebar->sendctl(top, "!size . -1 " + string minsz.x + " " + string minsz.y);
111	"ok" or
112	"help" =>
113		;
114	"rect" =>
115		r: Rect;
116		(c, next) = qword(req, next);
117		r.min.x = int c;
118		(c, next) = qword(req, next);
119		r.min.y = int c;
120		(c, next) = qword(req, next);
121		r.max.x = int c;
122		(c, next) = qword(req, next);
123		r.max.y = int c;
124		top.screenr = r;
125	"haskbdfocus" =>
126		in := int qword(req, next).t0 != 0;
127		cmd(top, "focus -global " + string in);
128		cmd(top, "update");
129	"task" =>
130		(r, nil) := splitqword(req, next);
131		if(r.t0 == r.t1)
132			req = sys->sprint("task %q", cmd(top, ".Wm_t.title cget -text"));
133		if(wmreq(top, c, req, next) == nil)
134			cmd(top, ". unmap; update");
135	"untask" =>
136		cmd(top, ". map; update");
137		return wmreq(top, c, req, next);
138	* =>
139		return wmreq(top, c, req, next);
140	}
141	return nil;
142}
143
144wmreq(top: ref Tk->Toplevel, c, req: string, e: int): string
145{
146	err := wmreq1(top, c, req, e);
147#	if(err != nil)
148#		sys->fprint(sys->fildes(2), "tkclient: request %#q failed: %s\n", req, err);
149	return err;
150}
151
152wmreq1(top: ref Tk->Toplevel, c, req: string, e: int): string
153{
154	name, reqid: string;
155	if(req != nil && req[0] == '!'){
156		(name, e) = qword(req, e);
157		(reqid, e) = qword(req, e);
158		if(name == nil || reqid == nil)
159			return "bad arg count";
160	}
161	if(top.ctxt.connfd != nil){
162		if(sys->fprint(top.ctxt.connfd, "%s", req) == -1)
163			return sys->sprint("%r");
164		if(req[0] == '!')
165			recvimage(top, name, reqid);
166		return nil;
167	}
168	if(req[0] != '!'){
169		(nil, nil, err) := wmlib->wmctl(top.ctxt, req);
170		return err;
171	}
172	# if there's no window manager, then we create a screen on the
173	# display image. there's nowhere to find the screen again except
174	# through the toplevel's image. that means that you can't create a
175	# menu without mapping a toplevel, and if you manage to unmap
176	# the toplevel without unmapping the menu, you'll have two
177	# screens on the same display image
178	# in the image, so
179	if(c != "!reshape")
180		return "unknown request";
181	i: ref Image;
182	if(top.image == nil){
183		if(name != ".")
184			return "screen not available";
185		di := top.display.image;
186		screen := Screen.allocate(di, top.display.color(Background), 0);
187		di.draw(di.r, screen.fill, nil, screen.fill.r.min);
188		i = screen.newwindow(di.r, Draw->Refbackup, Draw->Nofill);
189	}else{
190		if(name == ".")
191			i = top.image;
192		else
193			i = top.image.screen.newwindow(s2r(req, e).t0, Draw->Refbackup, Draw->Red);
194	}
195	tk->putimage(top, name+" "+reqid, i, nil);
196	return nil;
197}
198
199recvimage(top: ref Tk->Toplevel, name, reqid: string)
200{
201	i := <-top.ctxt.images;
202	if(i == nil){
203		cmd(top, name + " suspend");
204		i = <-top.ctxt.images;
205	}
206	tk->putimage(top, name+" "+reqid, i, nil);
207}
208
209settitle(top: ref Tk->Toplevel, name: string): string
210{
211	return titlebar->settitle(top, name);
212}
213
214handler(top: ref Tk->Toplevel, stop: chan of int)
215{
216	ctxt := top.ctxt;
217	if(stop == nil)
218		stop = chan of int;
219	for(;;)alt{
220	c := <-ctxt.kbd =>
221		tk->keyboard(top, c);
222	p := <-ctxt.ptr =>
223		tk->pointer(top, *p);
224	c := <-ctxt.ctl or
225	c = <-top.wreq =>
226		wmctl(top, c);
227	<-stop =>
228		exit;
229	}
230}
231
232snarfget(): string
233{
234	return wmlib->snarfget();
235}
236
237snarfput(buf: string)
238{
239	return wmlib->snarfput(buf);
240}
241
242cmd(top: ref Tk->Toplevel, s: string): string
243{
244	e := tk->cmd(top, s);
245	if (e != nil && e[0] == '!')
246		sys->fprint(sys->fildes(2), "tkclient: tk error %s on '%s'\n", e, s);
247	return e;
248}
249
250