micropolis-activity: fails to build with clang instead of gcc
[micropolis] / micropolisactivity.py
1 # -*- mode: python; tab-width: 4 -*-
2 #
3 # Micropolis, Unix Version. This game was released for the Unix platform
4 # in or about 1990 and has been modified for inclusion in the One Laptop
5 # Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If
6 # you need assistance with this program, you may contact:
7 # http://wiki.laptop.org/go/Micropolis or email micropolis@laptop.org.
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or (at
12 # your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful, but
15 # WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 # General Public License for more details. You should have received a
18 # copy of the GNU General Public License along with this program. If
19 # not, see <http://www.gnu.org/licenses/>.
20 #
21 # ADDITIONAL TERMS per GNU GPL Section 7
22 #
23 # No trademark or publicity rights are granted. This license does NOT
24 # give you any right, title or interest in the trademark SimCity or any
25 # other Electronic Arts trademark. You may not distribute any
26 # modification of this program using the trademark SimCity or claim any
27 # affliation or association with Electronic Arts Inc. or its employees.
28 #
29 # Any propagation or conveyance of this program must include this
30 # copyright notice and these terms.
31 #
32 # If you convey this program (or any modifications of it) and assume
33 # contractual liability for the program to recipients of it, you agree
34 # to indemnify Electronic Arts for any liability that those contractual
35 # assumptions impose on Electronic Arts.
36 #
37 # You may not misrepresent the origins of this program; modified
38 # versions of the program must be marked as such and not identified as
39 # the original program.
40 #
41 # This disclaimer supplements the one included in the General Public
42 # License. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS
43 # PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY
44 # OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF
45 # SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS
46 # DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES,
47 # INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY,
48 # FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY
49 # RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING,
50 # USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT AGAINST
51 # INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL
52 # MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE
53 # UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE
54 # WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE
55 # CORRECTED. NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR
56 # ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SOME
57 # JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED
58 # WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
59 # CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY
60 # NOT APPLY TO YOU.
61
62 import gtk
63 import os
64 import signal
65 import tempfile
66 import logging
67 import sys
68 import time
69 import subprocess
70 import thread
71 import fcntl
72
73 from sugar.activity import activity
74 from sugar.activity.activity import get_bundle_path
75 from sugar import profile
76 from gettext import gettext as _
77 from glob import glob
78
79 try:
80 import pygame.mixer
81 pygame.mixer.init()
82 except: pass
83
84 try:
85 from sugar.presence import presenceservice
86 except ImportError:
87 from sugar.presence import PresenceService as presenceservice
88
89
90 def QuoteTCL(s):
91 return s.replace('"', '\\"')
92
93
94 class MicropolisActivity(activity.Activity):
95
96 def __init__(self, handle):
97
98 activity.Activity.__init__(self, handle)
99
100 self.set_title(_('Micropolis Activity'))
101 self.connect('destroy', self._destroy_cb)
102 self.connect('focus-in-event', self._focus_in_cb)
103 self.connect('focus-out-event', self._focus_out_cb)
104
105 signal.signal(signal.SIGCHLD, self._sigchild_handler)
106
107 self._bundle_path = get_bundle_path()
108
109 if False:
110 # FIXME: Plug Micropolis's window into a gtk socket.
111 # Doesn't work yet, but it would be cool if it did.
112 socket = gtk.Socket()
113 try:
114 self.set_canvas(socket)
115 except AttributeError:
116 self.add(socket)
117 socket.show()
118 socket.connect('plug-added', self._plug_added_cb)
119 socket.connect('plug-removed', self._plug_removed_cb)
120
121 win = socket.get_id()
122
123 command = os.path.join(
124 self._bundle_path,
125 'Micropolis')
126
127 args = [
128 command,
129 #'-R', str(win), # Set root window to socket window id
130 '-t', # Interactive tty mode, so we can send it commands.
131 ]
132
133 logging.debug("CWD: " + self._bundle_path)
134 logging.debug("Micropolis ARGS: " + repr(args))
135
136 self._process = subprocess.Popen(
137 args,
138 stdin=subprocess.PIPE,
139 stdout=subprocess.PIPE,
140 close_fds=True,
141 cwd=self._bundle_path,
142 preexec_fn=lambda: os.chdir(self._bundle_path))
143
144 logging.debug("STARTING THREAD... " + str(self._stdout_thread_function))
145 t = None
146 try:
147 t = thread.start_new(
148 self._stdout_thread_function,
149 ())
150 except Exception, e:
151 logging.debug("EXCEPTION " + str(e))
152 self._stdout_thread = t
153 logging.debug("STARTED THREAD. " + str(t))
154
155 uri = handle.uri or ''
156 logging.debug("Micropolis SUGARSTARTUP URI " + repr(uri))
157 self.send_process(
158 'SugarStartUp "' + QuoteTCL(uri) + '"\n')
159
160 nick = profile.get_nick_name() or ''
161 logging.debug("Micropolis SUGARNICKNAME NICK " + repr(nick))
162 self.send_process(
163 'SugarNickName "' + QuoteTCL(nick) + '"\n')
164
165 #logging.debug("started Micropolis, pid " + repr(self._pid))
166
167 ps = presenceservice.get_instance()
168
169 for buddy in ps.get_buddies():
170 self._buddy_appeared_cb(ps, buddy)
171
172 ps.connect("buddy-appeared", self._buddy_appeared_cb)
173 ps.connect("buddy-disappeared", self._buddy_disappeared_cb)
174
175
176 def _stdout_thread_function(self, *args, **keys):
177 logging.debug("_stdout_thread_function BEGIN " + repr(args) + " " + repr(keys))
178 f = self._process.stdout
179 fcntl.fcntl(f.fileno(), fcntl.F_SETFD, 0)
180 while True:
181 line = 'XXX'
182 try:
183 line = f.readline()
184 except Exception, e:
185 logging.debug("READLINE EXCEPTION " + str(e))
186 break
187 logging.debug("LINE: " + repr(line))
188 line = line.strip()
189 if not line:
190 continue
191 words = line.strip().split(' ')
192 command = words[0]
193 if command == 'PlaySound':
194 logging.debug("PLAYSOUND " + " ".join(words[1:]))
195 self.play_sound(words[1])
196 else:
197 pass # logging.debug(">>> " + line)
198 logging.debug("_stdout_thread_function END")
199
200
201 def play_sound(self, name):
202 fileName = os.path.join(
203 self._bundle_path,
204 'res/sounds',
205 name.lower() + '.wav')
206 print "PLAY_SOUND " + fileName
207 try:
208 sound = pygame.mixer.Sound(fileName)
209 sound.play()
210 except Exception, e:
211 print "Can't play sound: " + fileName + " " + str(e)
212 pass
213
214
215 def send_process(self, message):
216 logging.debug("SEND_PROCESS " + message)
217 self._process.stdin.write(message)
218
219
220 def share(self):
221 logging.debug("SHARE")
222 Activity.share(self)
223 self.send_process(
224 'SugarShare\n')
225
226
227 def quit_process(self):
228 logging.debug("QUIT_PROCESS")
229 self.send_process(
230 'SugarQuit\n')
231 time.sleep(10)
232
233
234 def _plug_added_cb(self, sock):
235 logging.debug("Micropolis window opened")
236 return False
237
238
239 def _plug_removed_cb(self, sock):
240 logging.debug("Micropolis window closed")
241 self.destroy()
242 return False
243
244
245 def _destroy_cb(self, window):
246 logging.debug("Micropolis activity destroyed %r" % window)
247 self.quit_process()
248
249
250 def _focus_in_cb(self, window, event):
251 logging.debug("Micropolis activated %r %r" % (window, event))
252 self.send_process(
253 'SugarActivate\n')
254
255
256 def _focus_out_cb(self, window, event):
257 logging.debug("Micropolis deactivated %r %r" % (window, event))
258 self.send_process(
259 'SugarDeactivate\n')
260
261
262 def _buddy_appeared_cb(self, ps, buddy):
263
264 try:
265 key = buddy.props.key or ''
266 nick = buddy.props.nick or ''
267 color = buddy.props.color or ''
268 address = buddy.props.ip4_address or ''
269 except AttributeError:
270 key = buddy.get_name() or ''
271 nick = buddy.get_name() or ''
272 color = buddy.get_color() or ''
273 address = buddy.get_ip4_address() or ''
274
275 logging.debug("Micropolis _BUDDY_APPEARED_CB KEY " + repr(key) + " NICK " + repr(nick) + " COLOR " + repr(color) + " ADDRESS " + repr(address))
276
277 logging.debug("Buddy appeared " + repr(buddy.props.nick))
278
279 self.send_process(
280 'SugarBuddyAdd "' +
281 QuoteTCL(key) + '" "' +
282 QuoteTCL(nick) + '" "' +
283 QuoteTCL(color) + '" "' +
284 QuoteTCL(address) + '"\n')
285
286 def _buddy_disappeared_cb(self, ps, buddy):
287
288 try:
289 key = buddy.props.key or ''
290 nick = buddy.props.nick or ''
291 color = buddy.props.color or ''
292 address = buddy.props.ip4_address or ''
293 except AttributeError:
294 key = buddy.get_name() or ''
295 nick = buddy.get_name() or ''
296 color = buddy.get_color() or ''
297 address = buddy.get_ip4_address() or ''
298
299 logging.debug("Micropolis _BUDDY_DISAPPEARED_CB KEY " + repr(key) + " NICK " + repr(nick) + " COLOR " + repr(color) + " ADDRESS " + repr(address))
300
301 logging.debug("Buddy disappeared " + repr(buddy.props.nick))
302
303 self.send_process(
304 'SugarBuddyDel "' +
305 QuoteTCL(key) + '" "' +
306 QuoteTCL(nick) + '" "' +
307 QuoteTCL(color) + '" "' +
308 QuoteTCL(address) + '"\n')
309
310 def _sigchild_handler(self, signum, frame):
311 logging.debug("got signal %i %r %r" % (signum, frame, self._process))
312 sys.exit(0)
313
Impressum, Datenschutz