1 /****************************************************************************
2 Copyright (C) 1987-2015 by Jeffery P. Hansen
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18 Last edit by hansen on Sat Jan 3 22:18:22 2009
19 ****************************************************************************/
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include "tkgate.h"
24
25 #define SST_NONE 0 /* Nothing was selected */
26 #define SST_TIMESHIFT 1 /* Selected scope for time shifting */
27 #define SST_TRACE 2 /* Selecting an individual trace */
28
29 int scope_active = 0; /* Flag indicating that the scope is active */
30
31 static int scope_selectType = SST_NONE; /* Type of scope selection */
32 static int scope_clickValue = 0; /* x-position of mouse press if time shifting traces */
33 static GTrace *scope_traceSelect = 0; /* The selected trace for moving traces up/down */
34
35 extern GScope *Scope;
36
37 #if !TKGATE_BROKENCONFIGWIDGET
38 static Tk_ConfigSpec configSpecs[] = {
39 {TK_CONFIG_COLOR, "-background", "background", "Background",
40 "white", Tk_Offset(GScope,bgColor), 0, 0},
41 {TK_CONFIG_SYNONYM, "-bg", "background", 0, 0, 0, 0, 0},
42 {TK_CONFIG_SYNONYM, "-fg", "foreground", 0, 0, 0, 0, 0},
43 {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
44 "black", Tk_Offset(GScope,fgColor), 0, 0},
45 {TK_CONFIG_INT, "-height", "height", "Height",
46 STR(TKG_GATEWIN_HEIGHT), Tk_Offset(GScope,Height), 0, 0},
47 {TK_CONFIG_INT, "-width", "width", "Width",
48 STR(TKG_GATEWIN_WIDTH), Tk_Offset(GScope,Width), 0, 0},
49 {TK_CONFIG_STRING, "-xscrollcommand", "xscrollcommand", "Xscrollcommand",
50 "", Tk_Offset(GScope,xscroll), 0, 0},
51 {TK_CONFIG_STRING, "-yscrollcommand", "yscrollcommand", "Yscrollcommand",
52 "", Tk_Offset(GScope,yscroll), 0, 0},
53 {TK_CONFIG_END, 0, 0, 0, 0, 0, 0, 0}
54 };
55 #endif
56
scopeWinConfigure(Tcl_Interp * tcl,GScope * sw,int argc,const char * argv[],int flags)57 static int scopeWinConfigure(Tcl_Interp *tcl, GScope *sw, int argc, const char *argv[], int flags)
58 {
59 #if TKGATE_BROKENCONFIGWIDGET
60 sw->Width = 500;
61 sw->Height = 400;
62 sw->xscroll = ".scope.main.horz set";
63 sw->yscroll = ".scope.main.vert set";
64
65 Tk_SetWindowBackground(sw->win, XWhitePixelOfScreen(TkGate.S));
66 #else
67 if (Tk_ConfigureWidget(tcl, sw->win, configSpecs, argc, argv, (char*) sw, flags) != TCL_OK)
68 return TCL_ERROR;
69 Tk_SetWindowBackground(sw->win, sw->bgColor->pixel);
70 #endif
71
72 Tk_GeometryRequest(sw->win,sw->Width,sw->Height);
73
74 if (sw->gc == None) {
75 XGCValues gcv;
76
77 gcv.function = GXcopy;
78 gcv.graphics_exposures = False;
79 sw->gc = Tk_GetGC(sw->win,GCFunction|GCGraphicsExposures, &gcv);
80 }
81
82 return TCL_OK;
83 }
84
scopeWinEvent(ClientData data,XEvent * E)85 static void scopeWinEvent(ClientData data, XEvent *E)
86 {
87 if (E->type == DestroyNotify) {
88 ob_OMMode_t old_mode = ob_get_mode();
89
90 scope_active = 0;
91 ob_set_mode(OM_DISABLED);
92 ob_clear();
93 delete_GScope(Scope);
94 ob_set_mode(old_mode);
95 Scope = NULL;
96 }
97 ReqScopeRedisplay();
98 }
99
scopeYViewCommand(GScope * S,const char * command,const char * arg)100 static void scopeYViewCommand(GScope *S,const char *command,const char *arg)
101 {
102 int visTraces = (S->Height-ScopeBOTTOMHEIGHT)/ScopeTRACEHEIGHT;
103 int newStart = S->Start;
104 double f;
105 int n;
106
107 if (strcmp(command,"moveto") == 0) {
108 if (S->NumTraces) {
109 sscanf(arg,"%lf",&f);
110 newStart = S->NumTraces*(f+0.5/(double)S->NumTraces);
111 }
112 } else if (strcmp(command,"scroll") == 0) {
113 sscanf(arg,"%d",&n);
114 if (n < 0)
115 newStart--;
116 else if (n > 0)
117 newStart++;
118 }
119
120 if (newStart >= S->NumTraces-visTraces)
121 newStart = S->NumTraces-visTraces;
122 if (newStart < 0)
123 newStart = 0;
124
125 if (newStart != S->Start) {
126 S->Start = newStart;
127 ReqScopeRedisplay();
128 }
129 }
130
scopeXViewCommand(GScope * S,const char * command,const char * arg)131 static void scopeXViewCommand(GScope *S,const char *command,const char *arg)
132 {
133 int newLeftTime = S->s_leftTime;
134 double f;
135 int n;
136
137 if (strcmp(command,"moveto") == 0) {
138 int NI = S->s_interval ? (S->s_time/S->s_interval) : 0;
139 if (NI) {
140 sscanf(arg,"%lf",&f);
141 n = NI*(f+0.5/(double)NI);
142 if (n < 0) n = 0;
143 if (n > NI)
144 n = NI-1;
145 newLeftTime = n*S->s_interval;
146 }
147 } else if (strcmp(command,"scroll") == 0) {
148 sscanf(arg,"%d",&n);
149 if (n < 0)
150 newLeftTime -= S->s_interval;
151 else if (n > 0)
152 newLeftTime += S->s_interval;
153 }
154 if (newLeftTime > S->s_time)
155 newLeftTime = (S->s_time/S->s_interval)*S->s_interval;
156 if (newLeftTime < 0)
157 newLeftTime = 0;
158
159 if (newLeftTime != S->s_leftTime) {
160 S->s_leftTime = newLeftTime;
161 ReqScopeRedisplay();
162 }
163 }
164
GScope_setClickTime(GScope * S,int clickValue,double f)165 static void GScope_setClickTime(GScope *S,int clickValue,double f)
166 {
167 if (clickValue >= Scope->s_range*f)
168 S->s_leftTime = ((clickValue - S->s_range*f)/S->s_interval)*S->s_interval;
169 else
170 S->s_leftTime = 0;
171 }
172
scopeZoomCommand(GScope * S,const char * type,const char * arg)173 static void scopeZoomCommand(GScope *S,const char *type,const char *arg)
174 {
175 int d,r;
176
177 if (strcmp(type,"set") == 0) {
178 if (sscanf(arg,"%d",&d) != 1)
179 return;
180
181 r = S->s_baseRangePos+d;
182 } else {
183 r = S->s_rangePos;
184 if (sscanf(arg,"%d",&d) == 1)
185 r += d;
186 }
187
188
189
190 if (r < 0) r = 0;
191 if (r >= MAXRANGEPOS) r = MAXRANGEPOS-1;
192
193 if (r != S->s_rangePos) {
194 extern int RangeVals[];
195 int oldMiddle;
196 double f;
197
198 if (S->s_time < RangeVals[r]) {
199 oldMiddle = 0;
200 f = 0.0;
201 } else if (S->s_time <= S->s_leftTime + S->s_range) {
202 oldMiddle = S->s_time;
203 f = ((double)S->s_time-S->s_leftTime)/(double)S->s_range;
204 } else {
205 oldMiddle = S->s_leftTime + S->s_range/2;
206 f = 0.5;
207 }
208
209 S->s_rangePos = r;
210 S->s_range = RangeVals[S->s_rangePos];
211 GScope_pickInterval(&S->s_range,&S->s_interval,S->s_precision);
212
213 GScope_setClickTime(Scope,oldMiddle,f);
214
215 ReqScopeRedisplay();
216 }
217 }
218
scopeWinCommand(ClientData data,Tcl_Interp * tcl,int argc,const char * argv[])219 static int scopeWinCommand(ClientData data,Tcl_Interp *tcl,int argc,const char *argv[])
220 {
221 if (strcmp(argv[1],"yview") == 0) {
222 scopeYViewCommand(Scope,argv[2],argv[3]);
223 } else if (strcmp(argv[1],"xview") == 0) {
224 scopeXViewCommand(Scope,argv[2],argv[3]);
225 } else if (strcmp(argv[1],"zoom") == 0) {
226 scopeZoomCommand(Scope,"inc",argv[2]);
227 } else if (strcmp(argv[1],"setzoom") == 0) {
228 scopeZoomCommand(Scope,"set",argv[2]);
229 }
230
231 return TCL_OK;
232 }
233
234 /*
235 * Mouse was pressed on the name part of a logic trace so we should prepare to slide
236 * it up and down the list of traces.
237 */
ScopeWin_traceSelect(int x,int y,int px,int py,int n,int state)238 static void ScopeWin_traceSelect(int x,int y,int px,int py,int n,int state)
239 {
240 scope_traceSelect = GScope_hitTrace(Scope,y);
241 if (scope_traceSelect)
242 GScope_setTraceHighlight(Scope,scope_traceSelect);
243
244 /*
245 * Right mouse button
246 */
247 if (n == 3) {
248 if (scope_traceSelect)
249 DoTcl("PopupMenu::scopePost %d %d",px,py);
250 }
251 }
252
ScopeWin_moveTrace(int x,int y,int n,int state)253 static void ScopeWin_moveTrace(int x,int y,int n,int state)
254 {
255 if ((state & Button1MotionMask)) {
256 if (!scope_traceSelect) return;
257 GScope_moveTrace(Scope,scope_traceSelect,y);
258 GScope_postFullName(0);
259 } else {
260 GTrace *t = GScope_hitTrace(Scope,y);
261 GScope_postFullName(t);
262 }
263 }
264
ScopeWin_releaseTrace(int x,int y,int n,int state)265 static void ScopeWin_releaseTrace(int x,int y,int n,int state)
266 {
267 if (scope_traceSelect)
268 GScope_moveTrace(Scope,scope_traceSelect,y);
269 scope_traceSelect = 0;
270 }
271
272 /*
273 * Mouse was pressed on the logic trace part of the traces so we should prepare to slide
274 * the time line.
275 */
ScopeWin_timeSelect(int x,int y,int n,int state)276 static void ScopeWin_timeSelect(int x,int y,int n,int state)
277 {
278 int pmin = ScopeLEFTMARGIN;
279 int pmax = Scope->Width-ScopeRIGHTMARGIN;
280 extern int RangeVals[];
281 double f;
282
283 GScope_setTraceHighlight(Scope,0);
284
285 #if 0
286 if (x < pmin || x > pmax) {
287 scope_clickValue = -1;
288 return;
289 }
290 #endif
291
292 f = ((double)x-pmin)/(double)(pmax-pmin);
293 scope_clickValue = Scope->s_leftTime + Scope->s_range*f;
294
295 #if 1
296 if (scope_clickValue > Scope->s_time)
297 scope_clickValue = Scope->s_time;
298 #endif
299
300 if ((state & ControlMask)) {
301 int r = 0;
302
303 if (n == 1)
304 r = Scope->s_rangePos-1;
305 else if (n == 3)
306 r = Scope->s_rangePos+1;
307
308 if (r < 0) r = 0;
309 if (r >= MAXRANGEPOS) r = MAXRANGEPOS-1;
310
311 Scope->s_rangePos = r;
312 Scope->s_range = RangeVals[Scope->s_rangePos];
313 GScope_pickInterval(&Scope->s_range,&Scope->s_interval,Scope->s_precision);
314
315 GScope_setClickTime(Scope,scope_clickValue,f);
316 } else {
317 if (n == 1) {
318 GScope_setClickTime(Scope,scope_clickValue,f);
319 } else if (n == 3) {
320 GScope_setMark(Scope,x,1,state);
321 }
322 }
323
324 ReqScopeRedisplay();
325 }
326
327 /*
328 * Mouse motion that should slide the timeline.
329 */
ScopeWin_moveTime(int x,int y,int n,int state)330 static void ScopeWin_moveTime(int x,int y,int n,int state)
331 {
332 int pmin = ScopeLEFTMARGIN;
333 int pmax = Scope->Width-ScopeRIGHTMARGIN;
334 double f;
335
336 GScope_drawCrossLine(Scope,x);
337
338 /* printf("scopeButtonMotion(%d, %d)\n",x,y);*/
339 if (Scope->mark_count == 1) {
340 GScope_moveSelection(Scope);
341 }
342
343 if (!(state & Button1MotionMask)) return;
344
345 if (x < pmin || x > pmax || scope_clickValue < 0) return;
346 f = ((double)x-pmin)/(double)(pmax-pmin);
347
348 GScope_setClickTime(Scope,scope_clickValue,f);
349
350 ReqScopeTraceRedisplay();
351 }
352
ScopeWin_releaseTime(int x,int y,int n,int state)353 static void ScopeWin_releaseTime(int x,int y,int n,int state)
354 {
355 ScopeWin_moveTime(x,y,n,state);
356 if (n == 1)
357 ReqScopeRedisplay();
358 else if (n == 3)
359 GScope_setMark(Scope,x,0,state);
360 }
361
362
scopeButtonPress(int x,int y,int px,int py,int n,int state)363 void scopeButtonPress(int x,int y,int px,int py,int n,int state)
364 {
365 GScope_postFullName(0);
366
367 if (x < ScopeLEFTMARGIN) {
368 scope_selectType = SST_TRACE;
369 ScopeWin_traceSelect(x,y,px,py,n,state);
370 } else if (x < (Scope->Width-ScopeRIGHTMARGIN)) {
371 scope_selectType = SST_TIMESHIFT;
372 ScopeWin_timeSelect(x,y,n,state);
373 } else {
374 scope_selectType = SST_NONE;
375 scope_clickValue = -1;
376 }
377 }
378
scopeButtonMotion(int x,int y,int px,int py,int n,int state)379 void scopeButtonMotion(int x,int y,int px,int py,int n,int state)
380 {
381 switch (scope_selectType) {
382 case SST_TRACE :
383 ScopeWin_moveTrace(x,y,n,state);
384 GScope_postFullName(0);
385 break;
386 case SST_TIMESHIFT :
387 ScopeWin_moveTime(x,y,n,state);
388 GScope_postFullName(0);
389 break;
390 default:
391 if (x < ScopeLEFTMARGIN)
392 ScopeWin_moveTrace(x,y,n,state);
393 else {
394 ScopeWin_moveTime(x,y,n,state);
395 GScope_postFullName(0);
396 }
397 }
398 }
399
scopeButtonRelease(int x,int y,int px,int py,int n,int state)400 void scopeButtonRelease(int x,int y,int px,int py,int n,int state)
401 {
402 switch (scope_selectType) {
403 case SST_TRACE :
404 ScopeWin_releaseTrace(x,y,n,state);
405 break;
406 case SST_TIMESHIFT :
407 ScopeWin_releaseTime(x,y,n,state);
408 break;
409 default:
410 if (x < ScopeLEFTMARGIN)
411 ScopeWin_releaseTrace(x,y,n,state);
412 else
413 ScopeWin_releaseTime(x,y,n,state);
414 }
415
416 scope_selectType = SST_NONE;
417 }
418
419
420
scopeWinDestroy(ClientData data)421 void scopeWinDestroy(ClientData data)
422 {
423 }
424
425 /*****************************************************************************
426 *
427 * Create a scope window
428 *
429 *****************************************************************************/
gat_scope(ClientData data,Tcl_Interp * tcl,int argc,const char * argv[])430 int gat_scope(ClientData data, Tcl_Interp *tcl, int argc, const char *argv[])
431 {
432 Tk_Window root = (Tk_Window) data;
433 GScope *sw;
434 Tk_Window w;
435
436 if (argc < 2) {
437 Tcl_AppendResult(tcl, "wrong # args: should be \"",argv[0], " pathName ?options?\"", 0);
438 return TCL_ERROR;
439 }
440
441 w = Tk_CreateWindowFromPath(tcl, root, argv[1], 0);
442 if (!w) {
443 printf("gat_scope had window error\n");
444 return TCL_ERROR;
445 }
446 Tk_MakeWindowExist(w);
447 Tk_SetClass(w, "ScopeWin");
448
449 sw = new_GScope(TkGate.circuit->simulator.si_precision);
450 sw->win = w;
451 sw->d = Tk_Display(w);
452 sw->tcl = tcl;
453 sw->x = sw->y = 0;
454 sw->Width = TKG_GATEWIN_WIDTH;
455 sw->Height = TKG_GATEWIN_HEIGHT;
456 sw->gc = None;
457 sw->bgColor = 0;
458 sw->fgColor = 0;
459 sw->xscroll = 0;
460 sw->yscroll = 0;
461
462 Scope = sw;
463 TkGate.ScopeW = Tk_WindowId(sw->win);
464 GatePainter_setDrawable(TkGate.painterScopeW, Tk_WindowId(sw->win));
465
466 Tk_CreateEventHandler(w, ExposureMask|StructureNotifyMask, scopeWinEvent, sw);
467 Tcl_CreateCommand(tcl, Tk_PathName(w), scopeWinCommand, sw, scopeWinDestroy);
468 if (scopeWinConfigure(tcl, sw, argc-2, argv+2,0) != TCL_OK) {
469 Tk_DestroyWindow(w);
470 return TCL_ERROR;
471 }
472
473 Tcl_SetResult(tcl, Tk_PathName(w), TCL_STATIC);
474
475 scope_active = 1;
476
477 return TCL_OK;
478 }
479