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