|
|
1.1 root 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 [email protected].
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.