1 #include <locale.h>
2 #include "body.h"
3 #include "fallback.h"
4 #include "kconv.h"
5 #if USE_EDITRES
6 #include <X11/Xmu/Editres.h>
7 #endif
8 
9 Widget       toplevel;
10 XtAppContext app_context;
11 Body         *bd;
12 Score        *sc;
13 MenuBar      *mb;
14 TimerW       *tm;
15 Dimension    max_win_wid;
16 Dimension    max_win_hei;
17 char         *rcfile;
18 
19 static Window iconW;
20 static XtWorkProcId workproc_id = 0;
21 static XtIntervalId timeout_id = 0;
22 static int icon_state;
23 static struct timeval idletime;
24 
25 static XtActionsRec actions[] = {
26     { "menu", MenuAC },
27     { "PickupPiece", PickupPieceAC },
28     { "CancelPiece", CancelPieceAC }
29 };
30 static XrmOptionDescRec options[] = {
31     { "-fn",     "*fontList",         XrmoptionSepArg, NULL },
32     { "-score",  "*scoreOnly",        XrmoptionNoArg,  (XPointer)"True" },
33     { "-ctime",  "*connectLineTime",  XrmoptionSepArg,  NULL },
34     { "-cwidth", "*connectLineWidth", XrmoptionSepArg,  NULL },
35     { "-demo",   "*autoDemo",         XrmoptionNoArg,  (XPointer)"True" },
36     { "-mag",    "*magnifyFactor",    XrmoptionSepArg,  NULL },
37     { "-regular","*gameSize",         XrmoptionNoArg,  (XPointer)"0" },
38     { "-large",  "*gameSize",         XrmoptionNoArg,  (XPointer)"1" },
39     { "-huge",   "*gameSize",         XrmoptionNoArg,  (XPointer)"2" },
40     { "-trial",  "*trialMode",        XrmoptionNoArg,  (XPointer)"True" },
41     { "-gravity","*gravityMode",      XrmoptionNoArg,  (XPointer)"True" },
42     { "-KCONV",  "*kanjiConvert",     XrmoptionNoArg,  (XPointer)"True" }
43 };
44 
45 static XtResource gres[] = {
46     { "aboutString", "AboutString", XtRString, sizeof (char *),
47       0, XtRString, (XtPointer)"XShisen " XSHISEN_VERSION " by Masaoki Kobayashi" },
48     { "tedumari", "Tedumari", XtRString, sizeof (char *),
49       offsetof(GlobRes, tedumari), XtRString, (XtPointer)"You can get no more pieces." },
50     { "width", "Width", XtRInt, sizeof (int),
51       offsetof(GlobRes, Width), XtRString, (XtPointer)"640" },
52     { "height", "Height", XtRInt, sizeof (int),
53       offsetof(GlobRes, Height), XtRString, (XtPointer)"400" },
54     { "timeFormat", "TimeFormat", XtRString, sizeof (char *),
55       offsetof(GlobRes, timeFormat), XtRString, (XtPointer)"Your time is %2.2d:%2.2d." },
56     { "displayFormat1", "DisplayFormat1", XtRString, sizeof (char *),
57       offsetof(GlobRes, displayFormat1), XtRString, (XtPointer)"Rest: " },
58     { "displayFormat2", "DisplayFormat2", XtRString, sizeof (char *),
59       offsetof(GlobRes, displayFormat2), XtRString, (XtPointer)"Time: " },
60     { "scoreFile", "ScoreFile", XtRString, sizeof (char *),
61       offsetof(GlobRes, scoreFile), XtRString, (XtPointer)"%s/xshisen.scores" },
62     { "personalScore", "PersonalScore", XtRString, sizeof (char *),
63       offsetof(GlobRes, personalScoreFile), XtRString, (XtPointer)".xshisen.scores" },
64     { "scoreOnly", "ScoreOnly", XtRBoolean, sizeof (Boolean),
65       offsetof(GlobRes, scoreOnly), XtRString, (XtPointer)"False" },
66     { "connectLineColor", "ConnectLineColor", XtRPixel, sizeof (Pixel),
67       offsetof(GlobRes, connLineColor), XtRString, (XtPointer)"blue" },
68     { "connectLineTime", "ConnectLineTime", XtRInt, sizeof (int),
69       offsetof(GlobRes, connLineTime), XtRString, (XtPointer)"1000" },
70     { "connectLineWidth", "ConnectLineWidth", XtRInt, sizeof (int),
71       offsetof(GlobRes, connLineWidth), XtRString, (XtPointer)"6" },
72     { "autoDemo", "AutoDemo", XtRBoolean, sizeof (Boolean),
73       offsetof(GlobRes, autoDemo), XtRString, (XtPointer)"False" },
74     { "libraryDirectory", "LibraryDirectory", XtRString, sizeof (char *),
75       offsetof(GlobRes, libDir), XtRString, (XtPointer)LIB_DIR },
76     { "scoreDirectory", "ScoreDirectory", XtRString, sizeof (char *),
77       offsetof(GlobRes, scoreDir), XtRString, (XtPointer)DAT_DIR },
78     { "magnifyFactor", "MagnifyFactor", XtRFloat, sizeof (float),
79       offsetof(GlobRes, magFactor), XtRString, (XtPointer)"1.0" },
80     { "fitPixmap", "FitPixmap", XtRBoolean, sizeof (Boolean),
81       offsetof(GlobRes, fitPixmap), XtRString, (XtPointer)"True" },
82     { "colorCloseness", "ColorCloseness", XtRInt, sizeof (int),
83       offsetof(GlobRes, colorCloseness), XtRString, (XtPointer)"40000" },
84     { "gameSize", "GameSize", XtRInt, sizeof (int),
85       offsetof(GlobRes, gameSize), XtRString, (XtPointer)"0" },
86     { "trialMode", "TrialMode", XtRBoolean, sizeof (Boolean),
87       offsetof(GlobRes, trialMode), XtRString, (XtPointer)"False" },
88     { "gravityMode", "GravityMode", XtRBoolean, sizeof (Boolean),
89       offsetof(GlobRes, gravityMode), XtRString, (XtPointer)"False" },
90     { "idleTime", "IdleTime", XtRInt, sizeof (int),
91       offsetof(GlobRes, idleTime), XtRString, (XtPointer)"100000" },
92     { "kanjiCode", "KanjiCode", XtRString, sizeof (char *),
93       offsetof(GlobRes, kanjiCode), XtRString, (XtPointer)KANJICODE },
94     { "kanjiConvert", "KanjiConvert", XtRBoolean, sizeof (Boolean),
95       offsetof(GlobRes, kanjiConv), XtRString, (XtPointer)"False" },
96     { "imageSet", "ImageSet", XtRInt, sizeof (int),
97       offsetof(GlobRes, imageSet), XtRString, (XtPointer)"1" }
98 };
99 GlobRes globRes;
100 
101 static void
initrandom(void)102 initrandom(void)
103 {
104     struct timeval  tv;
105     struct timezone tz;
106 
107     gettimeofday(&tv, &tz);
108 #if HAVE_DRAND48
109     srand48(tv.tv_usec);
110 #elif HAVE_RANDOM
111     srandom(tv.tv_usec);
112 #elif HAVE_RAND
113     srand(tv.tv_usec);
114 #else
115     This line will cause error because all the random number
116     generating functions are not available!
117 #endif
118 }
119 
120 static Boolean
refreshtimer(XtPointer p)121 refreshtimer(XtPointer p)
122 {
123     if (bd->Active()) {
124         bd->HourlyPatrol();
125         tm->DisplayTimer(bd->GetRest());
126     } else {
127         tm->DisplayCurrentTime();
128     }
129     // This is a way not to consume too much CPU time
130     // just for such a silly game...
131 #if HAVE_USLEEP
132     usleep(idletime.tv_usec);
133 #elif HAVE_SELECT
134     select(0, NULL, NULL, NULL, &idletime);
135 #endif
136 
137     if (icon_state)
138         return True;
139     else
140         return False;
141 }
142 
143 static void
changeiconwindow(void)144 changeiconwindow(void)
145 {
146 #if DEBUG
147     fprintf(stderr, "Change icon window!\n");
148 #endif
149     XSetWindowBackgroundPixmap(XtDisplay(toplevel), iconW, (Pixmap)Mp[rand()%36]);
150     XClearArea(XtDisplay(toplevel), iconW, 0, 0, 0, 0, False);
151     timeout_id = XtAppAddTimeOut(app_context, 5000,
152                                  (XtTimerCallbackProc)changeiconwindow, NULL);
153 }
154 
155 static void
seticonwindow(void)156 seticonwindow(void)
157 {
158     Pixel  black;
159     unsigned int w, h;
160 
161     Mp[0].GetSize(w, h);
162     black = XBlackPixelOfScreen(XtScreen(toplevel));
163     iconW = XCreateSimpleWindow(XtDisplay(toplevel),
164                                 XRootWindowOfScreen(XtScreen(toplevel)),
165                                 0, 0, w, h, 1, black, black);
166     XtVaSetValues(toplevel,
167                   XtNbackground, black,
168                   XtNiconWindow, iconW,
169                   NULL);
170 }
171 
172 void
InitPicture()173 InitPicture()
174 {
175     char *lib_directory;
176     char subDir[16];
177 
178     if ((lib_directory = getenv("XSHISENLIB")) == NULL)
179         lib_directory = globRes.libDir;
180     sprintf(subDir, "s%d", globRes.imageSet);
181     InitGlobalMahjong(toplevel, lib_directory, subDir);
182 }
183 
184 void
statewatcher(Widget w,caddr_t unused,XEvent * event)185 statewatcher(Widget w, caddr_t unused, XEvent *event)
186 {
187     if (event->type == MapNotify) {
188         // de-iconified
189 #if DEBUG
190         fprintf(stderr, "Deiconified.\n");
191 #endif
192         workproc_id = XtAppAddWorkProc(app_context, (XtWorkProc)refreshtimer, NULL);
193         icon_state = 0;
194         if (timeout_id)
195             XtRemoveTimeOut(timeout_id);
196     }
197     else if (event->type == UnmapNotify) {
198         // iconified
199 #if DEBUG
200         fprintf(stderr, "Iconified.\n");
201 #endif
202         changeiconwindow();
203         icon_state = 1;
204     }
205 }
206 
207 static void
kanjiconvert(const char * operation)208 kanjiconvert(const char *operation)
209 {
210     char buffer[100], *p;
211     char *(*codeconv)(const char*);
212 
213     strncpy(buffer, operation, 99);
214     if (strchr(buffer, '-') == NULL) {
215         strcat(buffer, "-" KANJICODE);
216     }
217     if (strncasecmp(buffer, "jis-euc", 7) == 0)
218         codeconv = jis_to_euc;
219     else if (strncasecmp(buffer, "jis-sjis", 8) == 0)
220         codeconv = jis_to_sjis;
221     else if (strncasecmp(buffer, "euc-jis", 7) == 0)
222         codeconv = euc_to_jis;
223     else if (strncasecmp(buffer, "euc-sjis", 8) == 0)
224         codeconv = euc_to_sjis;
225     else if (strncasecmp(buffer, "sjis-jis", 8) == 0)
226         codeconv = sjis_to_jis;
227     else if (strncasecmp(buffer, "sjis-euc", 8) == 0)
228         codeconv = sjis_to_euc;
229     else {
230         // fprintf(stderr, "Don't know kanji conversion \"%s\"\n", buffer);
231         // exit(1);
232         codeconv = NULL;
233     }
234     while(fgets(buffer, 100, stdin) != NULL) {
235         // If the string "XSHISEN_VERSION" is found, it should be replaced
236         // with the XSHISEN_VERSION value.
237         if ((p = strstr(buffer, "XSHISEN_VERSION")) != NULL) {
238             int len1 = strlen(XSHISEN_VERSION);
239             char *p2 = p + strlen("XSHISEN_VERSION");
240             strncpy(p, XSHISEN_VERSION, len1);
241             p += len1;
242             memmove(p, p2, 100 - (p2 - buffer));
243         }
244         if (codeconv == NULL) {
245             fputs(buffer, stdout);
246         } else {
247             fputs(codeconv(buffer), stdout);
248         }
249     }
250 }
251 
252 static void
usage(const char * myname)253 usage(const char *myname)
254 {
255     printf("%s " XSHISEN_VERSION " by Masaoki Kobayashi\n\n", CLASS_NAME);
256     printf("Usage: %s [options]\n", myname);
257     printf("  -fn font   Sets fonts for menubar and time counter.\n");
258     printf("  -score     Popup high-score dialog and exit.\n");
259     printf("  -ctime N   Set the time that connection line remains (msec).\n");
260     printf("  -cwidth N  Set the connection line width (pixel).\n");
261     printf("  -demo      Auto demo mode for understanding rules.\n");
262     printf("  -mag F     Magnify window F times from original.\n");
263     printf("  -regular   Play regular game (default).\n");
264     printf("  -large     Play 2x game.\n");
265     printf("  -huge      Play 4x game.\n");
266     printf("  -trial     Play as \"Click Trial\".\n");
267     printf("  -gravity   Play with gravity.\n");
268     printf("\nAll other regular toolkit options are accepted.\n");
269     exit(2);
270 }
271 
272 int
main(int argc,char ** argv)273 main(int argc, char **argv)
274 {
275     char         *dat_directory;
276     char         *scorefile;
277     char         *home;
278     Display      *disp;
279     int          initial_game_state;
280     int          num_piece_x, num_piece_y;
281     Widget       topform, base;
282     XrmDatabase  rdb1, rdb2;
283 
284     setlocale(LC_ALL, "");
285     XtSetLanguageProc(NULL, NULL, NULL);
286 
287     initrandom();
288     toplevel = XtVaAppInitialize(&app_context, CLASS_NAME,
289                                  options, XtNumber(options),
290                                  &argc, argv,
291                                  fallback_resources,
292                                  XtNminWidth,         MIN_WIN_WID,
293                                  XtNminHeight,        MIN_WIN_HEI,
294                                  XtNallowShellResize, True,
295                                  NULL);
296     disp = XtDisplay(toplevel);
297 
298     // Override resources with $HOME/.xshisenrc file which is
299     // created by Body::WriteRcFile()
300     rdb1 = XrmGetDatabase(disp);
301     home = getenv("HOME");
302     rcfile = new char [strlen(home) + 12];
303     sprintf(rcfile, "%s/.xshisenrc", home);
304     rdb2 = XrmGetFileDatabase(rcfile);
305     XrmMergeDatabases(rdb2, &rdb1);
306     XrmSetDatabase(disp, rdb1);
307     XtVaGetApplicationResources(toplevel, (XtPointer)&globRes,
308                                 gres, XtNumber(gres), NULL);
309 
310     if (globRes.kanjiConv) {
311         kanjiconvert(argv[1]);
312         exit(0);
313     }
314     if (argc != 1)
315         usage(argv[0]);
316 
317     XtAddEventHandler(toplevel, StructureNotifyMask, False,
318                       (XtEventHandler)statewatcher,
319                       (Opaque)NULL);
320 #if USE_EDITRES
321     XtAddEventHandler(toplevel, (EventMask)0, True,
322                       (XtEventHandler)_XEditResCheckMessages,
323                       (Opaque)NULL);
324 #endif
325     XtAppAddActions(app_context, actions, XtNumber(actions));
326 
327     globRes.Width  = (int)(globRes.Width  * globRes.magFactor);
328     globRes.Height = (int)(globRes.Height * globRes.magFactor);
329 
330     initial_game_state = globRes.gameSize;
331     if (globRes.trialMode)
332         initial_game_state += NUM_GAME;
333     else if (globRes.gravityMode)
334         initial_game_state += 2*NUM_GAME;
335     idletime.tv_sec  = 0;
336     idletime.tv_usec = globRes.idleTime;
337     max_win_wid = DisplayWidth (disp, XDefaultScreen(disp));
338     max_win_hei = DisplayHeight(disp, XDefaultScreen(disp));
339     XtVaSetValues(toplevel,
340                   XtNmaxWidth,         max_win_wid,
341                   XtNmaxHeight,        max_win_hei,
342                   NULL);
343 
344     if ((dat_directory = getenv("XSHISENDAT")) == NULL)
345         dat_directory = globRes.scoreDir;
346 
347     sc = new Score(toplevel);
348     scorefile = new char[strlen(dat_directory)+strlen(globRes.scoreFile)+1];
349     sprintf(scorefile, globRes.scoreFile, dat_directory);
350     sc->SetScoreFile(scorefile, globRes.kanjiCode, globRes.personalScoreFile);
351     delete[] scorefile;
352 
353     if (globRes.scoreOnly) {
354         sc->DisplayScore(initial_game_state);
355 #if USE_MOTIF
356         XtAddCallback(*sc, XmNokCallback, (XtCallbackProc)ExitCB, NULL);
357 #else
358         XtAddCallback(XtNameToWidget(*sc, "*ok_button"),
359                       XtNcallback, (XtCallbackProc)ExitCB, NULL);
360 #endif
361     }
362     else {
363         GetGameSize(initial_game_state, num_piece_x, num_piece_y);
364 #if USE_MOTIF
365         topform = XtVaCreateManagedWidget("form", xmFormWidgetClass, toplevel,
366                                           NULL);
367         mb = new MenuBar(topform, initial_game_state % NUM_GAME,
368                          initial_game_state / NUM_GAME, globRes.imageSet - 1);
369         base = topform;
370 #else
371         topform = XtVaCreateManagedWidget("form", formWidgetClass, toplevel,
372                                           XtNdefaultDistance, 0,
373                                           NULL);
374         mb = new MenuBar(topform, initial_game_state % NUM_GAME,
375                          initial_game_state / NUM_GAME, globRes.imageSet - 1);
376         base = XtVaCreateManagedWidget("sform", formWidgetClass, topform,
377                                        XtNfromVert,        (Widget)*mb,
378                                        XtNleft,            XawChainLeft,
379                                        XtNright,           XawChainRight,
380                                        XtNtop,             XawChainTop,
381                                        XtNbottom,          XawChainBottom,
382                                        XtNresizable,       True,
383                                        XtNborderWidth,     0,
384                                        XtNdefaultDistance, 0,
385                                        NULL);
386 #endif
387         tm = new TimerW(base, *mb,
388                         globRes.displayFormat1, "000",
389                         globRes.displayFormat2, "00:00:00");
390         bd = new Body(initial_game_state, num_piece_x, num_piece_y, base, *tm);
391         workproc_id = XtAppAddWorkProc(app_context, (XtWorkProc)refreshtimer, NULL);
392         XtRealizeWidget(toplevel);
393         // InitGlobalMahjong must be called after realized because it
394         // requires the real Window.
395         InitPicture();
396         seticonwindow();
397         bd->SetGC();
398         GetBoardSizeFromGameSize(num_piece_x, num_piece_y,
399                                  globRes.Width, globRes.Height);
400         bd->SetGeometry(globRes.Width, globRes.Height);
401         SetGameStart();
402     }
403 
404     XtAppMainLoop(app_context);
405 }
406