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