Αυτό το commit περιλαμβάνεται σε:
infl00p 2022-03-23 20:14:33 +02:00
commit 8ec8e9bee2
451 αρχεία άλλαξαν με 46736 προσθήκες και 0 διαγραφές

63
content/articles/20/01_editorial.md Κανονικό αρχείο

@ -0,0 +1,63 @@
+++
title = 'Editorial'
date = '1999-12-01T00:00:00Z'
description = ''
author = 'Μιχάλης Καμπριάνης(mailto:kabrianis@hellug.gr)'
issue = ['Magaz 20']
issue_weight = 1
+++
----------------------------------------------------------------------------------------------------------------------------------------------------------------
*Καλώς ορίσατε στο **Magaz** Δεκεμβρίου \...*
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Φτάσαμε αισίως στο 20ο τεύχος, τελευταίο του 1999, και τελευταίο του δεύτερου έτους λειτουργίας του Magaz. Δεν θα πέσω στην παγίδα να πω τελευταίο της
χιλιετηρίδας (εξάλλου όπως όλοι ξέρουν, η χιλιετηρίδα ΔΕΝ τελειώνει το 1999 αλλά το 2000).
Εμείς περάσαμε και αυτόν τον χρόνο καλά και ελπίζουμε και εσείς μαζί μας. Την αναδρομή του 1999 θα την κάνουμε τώρα και όχι στο επόμενο τεύχος (21ο) γιατί λόγω
της αλλαγής έτους (το γνωστό πρόβλημα με την ημερομηνία στους υπολογιστές) και επειδή οι πιο πολλοί από εμάς δουλεύουμε σε σχετικές με υπολογιστές δουλειές, δεν
ξέρουμε πότε και πόσα θα μπορέσουμε να γράψουμε για το επόμενο έτος.
Σχετική με το πρόβλημα που αντιμετωπίζουν οι υπολογιστές με την αλλαγή του έτους, είναι και η δημοσκόπηση αυτού του μήνα.
To έτος αυτό λοιπόν κλείνει με 10 (προφανώς) τεύχη, 6 editorials, και 41 άρθρα. Επίσης, ερωτήθηκαν (και απαντήθηκαν) 205 ερωτήσεις στα πρώτα 6 τεύχη, πράγμα το
οποίο συνιστά abuse/misuse των διαθέσιμων resources και όχι use. Να υπενθυμίσω ότι όλο το 1998 (8 τεύχη με ερωτ-απαντήσεις) είχαμε 94 ερωτήσεις και 52 άρθρα. Ο
υπερδιπλασιασμός των ερωτήσεων επηρρέασε σαφέστατα τον διαθέσιμο χρόνο που είχαμε για άρθρα, και εφόσον οι ερωτήσεις επαναλαμβάνονταν ξανά και ξανά, εμείς
αποφασίσαμε να τις σταματήσουμε.
Μόλις σταμάτησαν λοιπόν οι ερωτήσεις, σταθεροποιηθήκαμε κι εμείς με την ησυχία μας στα 4 άρθρα το μήνα (συν ένα editorial) και βρήκαμε επιτέλους χρόνο να
ασχοληθούμε με το layout του περιοδικού, σύστημα δημοσκοπήσεων, δυνατότητα searching στα παλαιότερα τεύχη /ερωτήσεις του Magaz και πολλά άλλα features.
Συνολικά στον χρόνο που πέρασε φιλοξενήσαμε άρθρα από 16 άτομα, ενώ με τη βοήθεια άλλων 2 φίλων μεταφράσαμε τρία πολύ ενδιαφέροντα άρθρα που παρουσιάστηκαν στο
εξωτερικό.
Το Magaz αρχίζει να προσανατολίζεται προς τον πιο advanced χρήστη από ότι παλαιότερα, χωρίς βέβαια αυτό να σημαίνει ότι ξεχνάμε και τους νέους. Το γεγονός όμως
ότι αυτοί δεν κοιτάνε τα archives θα μας κάνει μάλλον να τους ξεχάσουμε. Ο καθένας επιλέγει την μοίρα του, και για να βοηθηθείς πρέπει πρώτα να βοηθήσεις τον
εαυτό σου ο ίδιος.
Σε αυτό το τεύχος έχουμε το πρώτο παιχνίδι που παρουσιάζεται στο Magaz, το [Civilization: Call to power](05_civ.html), μία [παρουσίαση του Basilisk II -
emulator για Macintosh](02_basilisk.html), μία [επεξήγηση του xinit](03_xinit.html) και ένα πρώτο μέρος για [προγραμματισμό σε GTK](04_gtk.html) που ξεκινάει
ένα αφιέρωμα του περιοδικού στον προγραμματισμό.
Να υπενθυμίσω ότι προγραμματιστικά θέματα έχουμε παρουσιάσει και στο παρελθόν με και με την πρώτη εισαγωγή , αλλά και παλιότερα με τις οδηγίες για
bash-scripting. Απλά τώρα θα δώσουμε ιδιαίτερη σημασία σε αυτό το θέμα, εφόσον το βρίσκουμε πολύ ενδιαφέρον και έχουμε ήδη προγραμματίσει αρθράκια για την perl
Τα άρθρα - προτάσεις του Magaz υπάρχουν και μεταβάλλονται τεύχος με το τεύχος, καθώς άλλα άρθρα \"καπαρώνονται\" και άλλα προσφέρονται.
- Κατηγορία Howto
- Callback στο Linux
- Προγραμματισμός σε QT
- Infrared communication
- SGML και LINUXDOC tutorial
- Κατηγορία αναλύσεις
- Linux και POSIX
- Το Linux σαν router
- Κατηγορία παρουσιάσεις
- Παρουσίαση του Koffice
- Παρουσιάσεις νέων Distributions
- Κατηγορία updates παλαιότερων άρθρων
- Update για Samba v2 και kernel 2.2
- Update για IP Masq σε kernel 2.2 και ipchains
Σας ευχόμαστε καλή ανάγνωση, καλά Χριστούγεννα και καλή χρονιά.

129
content/articles/20/02_basilisk.md Κανονικό αρχείο

@ -0,0 +1,129 @@
+++
title = 'Παρουσίαση του BasiliskII'
date = '1999-12-01T00:00:00Z'
description = ''
author = 'Αντώνης Μαυρέλος(mailto:A.Mavrelos@csd.ase.gr)'
issue = ['Magaz 20']
issue_weight = 2
+++
----------------------------------------------------------------------------------------------------------------------------------------------------------------
*Το BasiliskII είναι το καλύτερο πρόγραμμα εξομείωσης Mac στο Linux. Πόσο καλό είναι; Σκεφτείτε MacOS 8 να παίζει Quicktime video και να μπαίνετε στο Internet
μέσα από το Netscape.*
----------------------------------------------------------------------------------------------------------------------------------------------------------------
![](/20/img/9_sm.png)
[Μεγάλο μέγεθος](/20/img/9.png)
Τρέχει ό,τι δοκίμασα. Η ταχύτητα του είναι ικανοποιητική. Κάποιος μπορεί να δουλέψει με \"άνεση\" ακόμα και σε Celeron 333 που έχω εγώ. Μειονεκτήματα; Δεν κάνει
Emulation PPC επεξεργαστή. Κάνει emulation τα classics, και Mac II (εξού και το ΙΙ στο όνομα), ήχο (DSP), κάρτα δικτύου (δεν έχω δεν το δοκίμασα), CD-ROM (με
ήχο), SCSI (δεν έχω δεν το δοκίμασα).
**1. Installation**
----------------------------------------------
**2. Διαδικασία για εγκατάσταση MacOS 8**
--------------------------------------------------------------------
**3. Ενά τυπικό .basilisk\_ii\_prefs**
-----------------------------------------------------------------
**4. Δύο Screnshots**
------------------------------------------------
### [1. Installation]{#s1}
Την τελευταία έκδοση του BasiliskII θα τη βρείτε στη <http://www.uni-mainz.de/~bauec002/B2Main.html>. Είναι μόλις 555 ΚB (ούτε καν 666). Κάνετε compile με το
γνωστό πια τρόπο:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
./configure
make
make install
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Και το τρέχετε για πρώτη φορά. Το πρόγραμμα θα βγει (exit) αμέσως και θα δημιουργήσει ένα αρχείο στο home directory με όνομα .basilisk\_ii\_prefs. Αυτό είναι το
configuration file. Εδώ θα πρέπει να προσθέσουμε και να αλλάξουμε κάποιες εγγραφές. Οι περισσότερες είναι εύκολες στην κατανόηση τους. Υπάρχει και ένα Readme
file που εξηγεί τι κάνει η κάθε μία.\
Για αρχή να ορίσουμε ότι θέλουμε ο Mac μας να τρέχει σε παράθυτο 800Χ600. Προσθέτουμε Screen win/800/600. Τόσο απλά. Μετά θα πρέπει να δώσουμε το Path που
βρίσκεται η ROM του Mac μας. Τωρα το που θα βρείτε τη ROM δε θα σας το πω εγώ. Αλλά είναι σχετικά εύκολο να βγάλετε ένα image από ένα υπάρχον Mac και ακόμα πιο
εύκολο (αλλά όχι σωστό) να τη βρείτε στο Internet. Προσθέτουμε λοιπόν τη γραμμή: ROM \<path\>. Aν τρέξουμε το basilisk θα βγει το γνωστό εικονίδιο που βγάζουν
οι Mac όταν ξεκινάνε αλλά δε βρίσκουν δίσκο. Πάμε λοιπόν αμέσως μετά να δείξουμε στο Mac μας τον/τους δίσκους του. Εδώ έχουμε 2 λύσεις. Να δώσουμε ολόκληρο
filesystem ή αυτό που έκανα εγώ, ένα image file.\
Δημιουργούμε με την dd ένα αρχείο μεγέθους ας πούμε 250 ΜΒ.
dd if=/dev/zero of=/usr/local/BasiliskII/Mac_HD.hfs bs=1k count=250000
Κατεβάζουμε τα HFSUtils και τα κάνουμε compile. Κάνουμε mount to image που μόλις φτιάξαμε:
hmount /var/local/basiliskII/Mac_HD.hfs
Αν όλα πήγαν καλά με την hvol θα βλέπουμε το Mac\_HD Φορμάρουμε το filesystem με:
hformat -l "Local MacHD" /usr/local/BasiliskII/Mac_HD.hfs
Για να ξεκινήσει ο Mac θέλει φυσικά και λειτουργικό σύστημα. Η δοκιμή που θα κάνω (αν βρω ελεύθερο χρόνο) θα είναι να του βάλω Linux. Προς το παρόν καταφεύγουμε
στο MacOS. Οι επιλογές μου (αυτά είχα) είναι τα 7.0 και 7.1. Προς το παρόν εξομοιώνουμε τον classic. Αυτο το ορίζουμε με την παράμετρο modelid 5 στο
.basiliskII\_prefs. Κάνουμε boot τον Mac και χρησιμοποιούμε τις δισκέτες εγκατάσταης - ή όσοι έχουν την παρανομία στο αίμα τους, βρίσκουν ένα disk image με
κάποιο MacOS στο internet. Eίστε έτοιμοι. Αν θέλετε να προσθέσετε αρχεία στον εικονικό δίσκο σας χρησιμοποιήτε την hcopy
hcopy ~/norton_utilities.hpx :
Θα αντιγραφούν τα norton στον δίσκο που αυτή τη στιγμή είναι mount (current). Το ποιος είναι το βλέπουμε με hvol ή κάνουμε cat το  /hcwd Σημειώστε ότι μετά από
reboot του host συστήματος (Linux) δεν χάνετε το current mounted filesystem. Και αυτό είναι φυσικό αφού είναι γραμμένο στο .hcwd.
### [2. Διαδικασία για εγκατάσταση MacOS 8]{#s2}
Καταρχήν δε μπορούμε να μεταπηδήσουμε από το 7.0 ή 7.1 απευθείας στο 8. Τα βήματα είναι τα εξής:
- Βάζουμε ένα σύστημα τουλάχιστον 7.5.3
- Βγαίνουμε από τον emulator και αλλάζουμε το ID του συστήματος σε 14 (Quadra).
- To 8 γίνεται εγκατάσταση από CD. Το τοποθετούμε στο tray και τρέχουμε τον installer.
Προσέχουμε να μην κάνει εγκατάσταση τους drivers για τον σκληρό δίσκο. Για κάποιο λόγο καθυστερεί (δεν κολάει) υπερβολικά. Το άφησα 2 ώρες σε αυτό το σημείο και
μετά διέκοψα την εγκατάσταση. Αλλη μια περίπτωση που καθυστερεί υπερβολικά είναι όταν έχουμε φτιάξει ένα μεγάλο filesystem και αυτό αρχίζει να γεμίζει. Κατά τα
άλλα όλες σχεδόν οι λειτουργίες στο 8 δουλεύουν άψογα. Το τρομερό είναι στα quicktime videos που παίζουν χωρίς frame skiping και χωρίς απώλειες στον ήχο. Τα
tutorials (multimedia), τα παιχνίδια, όλα παίζουν τέλεια. Ετρεξα μια χαρά το Quicken, αρκετά extentions και shareware. Λίγες φορές έχει κολλήσει και τότε μου
έβγαλε το μήνυμα: \"Your Mac just did something very stupid\".
### [3. Ενά τυπικό .basilisk\_ii\_prefs]{#s3}
----------------------------------------------------------------------------------------------------------------------------------------------------------------
seriala /dev/ttyS0
serialb /dev/ttyS1
bootdrive 0
bootdriver 0
ramsize 33554432
frameskip 6
modelid 14
fpu false
nocdrom false
nosound false
nogui false
keycodes false
screen win/800/600
rom /usr/local/BasiliskII/rom
disk /var/BasiliskII/Mac_HD.hfs
----------------------------------------------------------------------------------------------------------------------------------------------------------------
### [4. Δύο Screnshots]{#s4}
![](/20/img/2_sm.png)
[Μεγάλο μέγεθος](/20/img/2.png)
![](/20/img/12_sm.png)
[Μεγάλο μέγεθος](/20/img/12.png)

128
content/articles/20/03_xinit.md Κανονικό αρχείο

@ -0,0 +1,128 @@
+++
title = 'Aρχεία εκκίνησης των X'
date = '1999-12-01T00:00:00Z'
description = ''
author = 'DJ Art(mailto:djart@hellug.gr)'
issue = ['Magaz 20']
issue_weight = 3
+++
----------------------------------------------------------------------------------------------------------------------------------------------------------------
*Το άρθρο αυτό έχει σκοπό να σας παρουσιάσει τα ιδιαίτερα εκείνα αρχεία, με τα οποία μπορείτε να παραμετροποιήσετε την είσοδό σας στα X-Windows, αναλύοντας με
ποιόν τρόπο θα \"πειράξετε\" τα αρχεία εκκίνησης των X.*
----------------------------------------------------------------------------------------------------------------------------------------------------------------
**1. Εισαγωγή**
---------------------------------------
- [1.1 Runlevels](#ss1.1)
**2. Αρχεία εκκίνησης**
-----------------------------------------------
- [2.1 Το αρχείο xinitrc (runlevel: single/multi user mode)](#ss2.1)
- [2.2 Συντεταγμένες](#ss2.2)
- [2.3 Το αρχείο xsession (runlevel: X11)](#ss2.3)
**3. Συντεταγμένες για πιο προχωρημένους**
------------------------------------------------------------------
### [1. Εισαγωγή]{#s1}
Όταν μιλάμε για \"εκκίνηση των X\" εννοούμε με λίγα λόγια, τον window manager που επιθυμούμε να χρησιμοποιούμε στα X και διάφορες εφαρμογές ή προγράμματα που
θέλουμε να ξεκινούν αυτόματα κατά την είσοδό μας στο σύστημα των X-Windows. Επισης, πολλές φορές δεν θέλουμε απλά να ξεκινάει αυτόματα ένα πρόγραμμα, αλλά να
εμφανίζεται και σε ένα συγκεκριμένο σημείο στην επιφάνεια εργασίας μας.
### [1.1 Runlevels]{#ss1.1}
Στο Linux υπάρχουν δύο τρόποι για να ξεκινήσουν τα X-Windows: ο ένας είναι να bootάρουμε κανονικά το λειτουργικό, να κάνουμε login, μπαίνοντας στην κονσόλα, και
μετά, αν θέλουμε X-Windows, να πληκτρολογήσουμε την εντολή **startx**. Ο δεύτερος τρόπος είναι να bootάρουμε το Linux και αμέσως μετά το φόρτωμα του πυρήνα, να
φορτώσουν τα X-Windows, οπότε να γίνει η διαδικασία του login από το γραφικό περιβάλλον. Στις δύο αυτές περιπτώσεις, υπάρχουν, δυστυχώς, διαφορετικά αρχεία
εκκίνησης για τα X. Κατ\' αυτόν τον τρόπο, αν μιά μέρα κανείς θελήσει να αλλάξει τον τρόπο με τον οποίο μπαίνει στα X, θα εκπλαγεί, διαπιστώνοντας ότι αυτά που
πιθανόν να έχει ρυθμίσει να εκτελούνται κατά την είσοδό του στο γραφικό περιβάλλον, δεν υπάρχουν πιά.
Οι δύο παραπάνω τρόποι εισόδου στα X-Windows, χαρακτηρίζονται με δύο διαφορετικά runlevels. Ο μεν πρώτος τρόπος, μπορεί να είναι runlevel σε single user mode,
σε multiuser mode χωρίς δίκτυο, ή σε full multiuser mode. Ο δεύτερος τρόπος πραγματοποιείται με την επιλογή του runlevel σε X11 mode. Τα runlevels ορίζονται στο
αρχείο **/etc/inittab** με αριθμούς. Κάθε αριθμός αντιστοιχεί σε ένα runlevel. Αξιοσημείωτο, όμως είναι, ότι κάθε Linux distribution ορίζει με διαφορετικούς
αριθμούς το κάθε runlevel. Μην ανησυχείτε, στο αρχείο **/etc/inittab**, συνήθως περιγράφονται σε comment mode, οι αντιστοιχίες αριθμων και runlevels.
**ΠΡΟΣΟΧΗ:** Υπάρχουν, αν δεν το έχετε καταλάβει, τα αντίστοιχα runlevel για το reboot και το shutdown του λειτουργικού (αυτά συνήθως εκφράζονται με τους
αριθμούς 6 και 0 αντίστοιχα, σε όλα τα distribution). Έτσι, δεν πρέπει να ορίσετε το προεπιλεγμένο runlevel σας σε κάποιο από τα δύο αυτά runlevel, για
ευνόητους λόγους (μόλις θα ξεκινάτε το Linux, ή θα γίνεται reboot ή shutdown).
Ας δούμε όμως πως ορίζουμε το default runlevel, με το οποίο θα ξεκινά το Linux. Μέσα στο αρχείο **/etc/inittab**, υπάρχει η ακόλουθη γραμμή:
id:3:initdefault:
Η γραμμή ορίζει σαν προεπιλογή το runlevel 3 (το οποίο στο RedHat είναι το full multiuser). Αλλάξτε, λοιπόν τον αριθμό με τον αριθμό του runlevel που
επιθυμείτε.
### [2. Αρχεία εκκίνησης]{#s2}
### [2.1 Το αρχείο xinitrc (runlevel: single/multi user mode)]{#ss2.1}
Όπως θα καταλάβατε και από τον τίτλο, εδώ θα μιλήσουμε για τις ρυθμίσεις που μπορείτε να πραγματοποιήσετε, όταν χρησιμοποιείτε για runlevel το single ή το multi
user mode.
Το αρχείο που θα μας απασχολήσει εδώ είναι το **xinitrc**, το οποίο βρίσκεται στο home directory του χρήστη, και μάλιστα, είναι κρυφό. Επομένως η πλήρης
διαδρομή του είναι **\~/.xinitrc** (το **\~** συμβολίζει το home directory. Θα μπορούσα στη θέση του να χρησιμοποιήσω τη μεταβλητή **\$HOME**).
Μέσα στο αρχείο αυτό, όπως είπαμε, μπορούμε να ορίσουμε ποιόν window manager θέλουμε να χρησιμοποιήσουμε. Έτσι, αν θέλουμε για παράδειγμα να δουλεύουμε στο KDE,
γράφουμε μέσα στο αρχείο (αφού το ανοίξουμε με έναν text editor) **startkde**. Αν θέλαμε τον Window Maker, θα γράφαμε **wmaker**, αν θέλαμε το GNOME, θα γράφαμε
**gnome-session**, κ.ο.κ.
Για να δούμε τώρα τι μπορούμε να κάνουμε, ώστε όταν ξεκινάν τα X, να ανοίγουν ταυτόχρονα και τα προγράμματα που εμείς θέλουμε. Στο αρχείο .xinitrc, γράφουμε την
εντολή που θέλουμε (η οποία αντιπροσωπεύει το πρόγραμμά μας) και δίπλα βάζουμε το σύμβολο **&**. Βάζουμε το σύμβολο αυτό για να τρέξει η εφαρμογή στο background
και να επιτρέψει να δουλέψει ο window manager. Οπότε, όπως θα καταλάβατε, πάντα θα γράφουμε τις εντολές που θέλουμε πρώτα, και φυσικά κάθε εντολή θα
καταλαμβάνει καινούρια γραμμή στο αρχείο μας (με το σύμβολο & πάντα) και στο τέλος θα βάζουμε τον window manager που θέλουμε (χωρίς το σύμβολο &). Για
παράδειγμα, το ακόλουθο .xinitrc μας επιτρέπει να ξεκινήσουμε τα X, να ανοίξει το πρόγραμμα Xisp, το πρόγραμμα rxvt και όλα αυτά κάτω από το περιβάλλον του KDE:
**xisp &\
rxvt &\
startkde**
### [2.2 Συντεταγμένες]{#ss2.2}
Σειρά τώρα έχει να εξηγήσουμε τί μπορούμε να κάνουμε, ώστε τα προγράμματα που βάλαμε να ξεκινάν με το φόρτωμα των X, να εμφανίζονται σε ένα προκαθορισμένο
σημείο της επιφάνειας εργασίας. Η επιφάνεια εργασίας μας έχει συντεταγμένες. Η πάνω αριστερή γωνία έχει τις συντεταγμένες (0,0). Οι συντεταγμένες, γενικά, στην
περίπτωση των X συμβολίζονται ως +0+0. Ανάλογα, λοιπόν με το ποιά ανάλυση έχουμε επιλέξει στα X, ανάλογο είναι και το εύρος των συντεταγμένων. Το πρώτο στοιχείο
στις συντεταγμένες είναι η τετμημένη, δηλαδή μας ορίζει την οριζόντια θέση μας. Το δεύτερο στοιχείο είναι η τεταγμένη και μας ορίζει την κάθετη θέση μας
(ουσιαστικά και στα δύο στοιχεία ο αριθμός υποδηλώνει την απόστασή μας σε pixels από την πάνω αριστερή γωνία). Ευτυχώς για να μην μπλέξουμε, όταν κάποιος
μετακινεί ένα παράθυρο στην επιφάνεια εργασίας του, εμφανίζονται αυτόματα οι συντεταγμένες της πάνω αριστερής γωνίας του ανοιχτού παράθυρου. ΠΡΟΣΟΧΗ: μπορεί ο
window manager που χρησιμοποιείτε ήδη, να μην εμφανίζει τις συντεταγμένες. Π.χ. το KDE δεν τις εμφανίζει, αλλά μπορείτε να χρησιμοποιήσετε έναν άλλο window
manager, όπως fvwm ή Window Maker, για να προσδιορίσετε της συντεταγμένες μιας θέσης.
Ας υποθέσουμε, λοιπόν, στο προηγούμενο παράδειγμα, πως θέλετε όταν ανοίγετε τα X, να ανοίγει το Xisp στην πάνω αριστερή γωνία της οθόνης περίπου, το rxvt να
ανοίγει περίπου στο μέσον της οθόνης, και όλα αυτά χρησιμοποιώντας για window manager το GNOME. Το αρχείο μας θα γίνει:
**xisp -geometry +5+5 &\
rxvt -geometry +100+67 &\
gnome-session**
Όπως θα καταλάβατε, για να ορίσετε σε ποιές συντεταγμένες θα ανοίγει ένα πρόγραμμα, αρκεί να το εκτελείτε με την παράμετρο **-geometry +x+y**, όπου **x** και
**y** είναι οι επιθυμητές συντεταγμένες.
### [2.3 Το αρχείο xsession (runlevel: X11)]{#ss2.3}
Για να εξηγήσουμε τώρα, τί γίνεται όταν το runlevel μας φορτώνει τα X αμέσως μετά το boot του πυρήνα. Δυστυχώς, το αρχείο .xinitrc που φτιάξαμε νωρίτερα, δεν
δουλεύει σ\' αυτήν την περίπτωση. Αντί αυτού του αρχείου, αρκεί να φτιάξουμε ένα αρχείο στο home directory και να το ονομάσουμε **.xsession**. Για να
παραμετροποιήσουμε αυτά που θέλουμε κατά την εκκίνηση, ακολουθούμε ακριβώς τα βήματα που ακολουθήσαμε και για το παραπάνω αρχείο. **ΠΡΟΣΟΧΗ**, όμως, υπάρχει μια
μικρή διαφορά: το αρχείο **.xsession** που δημιουργήσαμε, πρέπει να είναι executable, αλλιώς δεν δουλεύει. Για να το κάνουμε executable, απλώς εκτελούμε την
εξής εντολή: **chmod u+x .xsession**.
Τελειώσαμε !! Από εδώ και μπρός θα είστε ικανοί να παραμετροποιήσετε την είσοδό σας στα X-Windows.
### [3. Συντεταγμένες για πιο προχωρημένους]{#s3}
Πρίν κλείσω το άρθρο, θα ήθελα να σας πώ λίγα ακόμα πράγματα για τις συντεταγμένες της επιφάνειας εργασίας μας.
Όπως ανέφερα προηγούμενα, οι συντεταγμένες +0+0 ορίζουν την πάνω αριστερή γωνία της οθόνης μας. Η πάνω δεξιά γωνία χαρακτηρίζεται από τις συντεταγμένες -0+0. Η
κάτω δεξιά γωνία με τις -0-0 και η κάτω αριστερή γωνία με τις +0-0.
Είναι λοιπόν αυτονόητο, πως αν κάποιος εφαρμόσει την \"Ευκλείδιο Γεωμετρία\" στην επιφάνεια εργασίας του, θα καταφέρει να προσδιορίζει με μεγαλύτερη ακρίβεια
μια συγκεκριμένη θέση. Αν μπερδευτήκατε με όλα αυτά λοιπόν, θα σας συμβούλευα να αρκεστείτε στο +0+0.

518
content/articles/20/04_gtk.md Κανονικό αρχείο

@ -0,0 +1,518 @@
+++
title = 'Προγραμματισμός με GTK(\...)'
date = '1999-10-01T00:00:00Z'
description = ''
author = 'Παπαδογιαννάκης Βαγγέλης για το Magaz ( magaz.hellug.gr(http://magaz.hellug.gr) )'
issue = ['Magaz 20']
issue_weight = 4
+++
----------------------------------------------------------------------------------------------------------------------------------------------------------------
*GTK\... Gimp ToolΚit. Αυτό το πρόγραμμα τα άρχισε όλα. Από τη στιγμή που βγήκε το gimp, δημιουργήθηκαν ένα σωρό εφαρμογές που χρησιμοποιούν τις βιβλιοθήκες
απεικόνισής του, οι οποίες σημειωτέον είναι από τις καλύτερες που υπάρχουν. Πάνω σε αυτή βασίζεται ολόκληρο το gnome, και είναι από τα προτιμούμενα της REDHAT
και άλλων φυσικά διανομών. \`Ελα όμως που ο προγραμματισμός σε GTK είναι σχετικά δύσκολος\... Μη μασάτε, δεν είναι και τόσο όσο νομίζετε\... Είναι
ευκολούτσικος, και δεν θα σας πάρει πάνω από 10 λεπτά διάβασμα για να καταφέρετε να στήσετε το πρώτο σας προγραμματάκι σε GTK. Πολλά από τα σημεία αυτού του
άρθρου, έχουν βασιστεί στα tutorials της gtk. όπως αυτά έρχονται με το gtk-devel.rpm πακέτο. Για περισσότερες πληροφορίες σε σημεία του κειμένου, παρακαλώ ρίξτε
τους μια ματιά.*
----------------------------------------------------------------------------------------------------------------------------------------------------------------
**1. Περί αυτού του άρθρου. (the boring stuff)**
----------------------------------------------------------------------
**2. Που θα βρείτε το GTK**
-------------------------------------------------
**3. Προϋποθέσεις**
-----------------------------------------
**4. Βασικές έννοιες προγραμματισμού σε gtk**
-------------------------------------------------------------------
- [4.1 Βασικά χαρακτηριστικά](#ss4.1)
- [4.2 widgets, signals, κ.λπ.](#ss4.2)
- [4.3 Γεια σας μάγκες! Το πρώτο πρόγραμμα σε gtk](#ss4.3)
- [4.4 Εξήγηση του Hello Magez](#ss4.4)
- [4.5 Πως θα το τρέξετε.](#ss4.5)
**5. Tοποθέτηση των widgets**
---------------------------------------------------
- [5.1 Boxes, ή αλλιώς κουτιά :)](#ss5.1)
- [5.2 Tοποθέτηση των widgets, partII (λέγε με tables)](#ss5.2)
- [5.3 Η συνέχεια](#ss5.3)
### [1. Περί αυτού του άρθρου. (the boring stuff)]{#s1}
Αυτό το άρθρο, δεν έχει σκοπό να σας μάθει gtk, αλλά να σας εξοικειώσει με τις δυσκολότερες έννοιες στο toolkit αυτό, ώστε να αρχίσετε μόνοι σας να
προγραμματίζετε. Σε καμία περίπτωση δεν είμαι υπεύθυνος εγώ ειδικά και το magaz γενικότερα αν κάνετε μπάχαλο τον υπολογιστή σας. Στο δικό μου δουλεύουν όλα μια
χαρά, και δεν έπαθε τίποτα από όσα γράφω σε αυτό το άρθρο.
\`Εχω προγραμματίσει να ολοκληρωθεί σε τρεις συνέχειες. Πολύ πιθανόν και 4. Αυτό δεν σημαίνει ότι είμαι υποχρεωμένος να συνεχίσω και με τα άλλα, αλλά δεσμεύομαι
να προσπαθήσω. Και αυτό το λεω, γιατί στο παρελθόν είχα κάνει κάτι αντίστοιχό που δεν ολοκληρώθηκε (bash, enlightenment) αλλά είχα δικαιολογία και για τα δύο.
Μπορεί κάτι να συμβεί και να μη συνεχιστεί. Σε μια τέτοια περίπτωση, παρακαλώ όποιος έχει την όρεξη να συνεχίσει.
Επίσης, δεν υπάρχει ούτε ΜΙΑ πιθανότητα να μην έχω κάνει εκφραστικά -και ίσως νοηματικά- λαθάκια. Είναι η δυσκολία που υπάρχει στη μετάφραση αυτού που θέλουμε
να εκφράσουμε, καθώς και η μεταφορά του σε λέξεις. \`Οσοι από εσάς προγραμματίζουν, ή έχουν τέλος πάντων μια εξοικείωση με την αγγλική ορολογία, καταλαβαίνετε
τι θέλω να πω. Να είσαστε λοιπόν ελαστικοί στην κρίση σας.
\`Οσοι από εσάς ασχολούνται με gtk και βρουν τίποτα ανακρίβειες (πράγμα πολύ πιθανό γιατί ναι μεν προγραμματίζω σε gtk αλλά δεν χρησιμοποιώ όλα όσα εξηγώ σε
αυτό το άρθρο), παρακαλώ να μου στείλετε με [mail στο papas\@hellug.gr](mailto:papas@hellug.gr) τις παρατηρήσεις σας, και θα προσπαθήσω να διορθώσω τα λάθη σε
ένα από τα επόμενα άρθρα.
Καλό διάβασμα, και happy programming!
### [2. Που θα βρείτε το GTK]{#s2}
Κατά πάσα πιθανότητα, το έχετε ήδη εγκατεστημένο στον υπολογιστή σας. Για όσους από εσάς αρνούνται να εγκαταστήσουν το GTK, επιμένοντας σε υποκατάστατα του
τύπου QT - καλά, μη βαράτε :) - σας παραπέμπω στη διεύθυνση <http://www.gtk.org> να το κατεβάσετε σε ότι μορφή θέλετε. Αν θέλετε **rpm**, μην ξεχάσετε να πάρετε
και το **devel** πακέτο, γιατί αυτό ουσιαστικά θα μας χρειαστεί ώστε να κάνουμε τα προγράμματά μας να τρέχουν.
### [3. Προϋποθέσεις]{#s3}
Δεν υπάρχουν ουσιαστικές προϋποθέσεις. Απλά θα πρέπει να ξέρετε τα βασικά του προγραμματισμού, λίγη C, και να έχετε πολύ όρεξη. Επίσης, θα πρέπει να έχετε στο
PATH σας το script **gtk-config**, για να αποφύγουμε να βρίσκουμε λεπτομέρειες που είναι διαφορετικές για κάθε μηχάνημα. Περισσότερες πληροφορίες θα βρείτε στο
**man page** του gtk-config (`man gtk-config`)
### [4. Βασικές έννοιες προγραμματισμού σε gtk]{#s4}
Ας ξεκινήσουμε με τις αρχικές έννοιες, που στη συνέχεια θα μας βοηθήσουν στην κατανόηση του προγραμματισμού σε gtk.
### [4.1 Βασικά χαρακτηριστικά]{#ss4.1}
Κάθε πρόγραμμα που θα φτιάχνουμε, πρέπει να περιέχει, μεταξύ άλλων, και τα εξής:
Πρώτα από όλα, πρέπει να περιέχει τις βιβλιοθήκες που θα κάνουμε import. Για την gtk, αυτή είναι ή `gtk.h`, που βρίσκεται μέσα στο directory **gtk**
----------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <gtk/gtk.h>
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Φυσικά, όπως όλα τα προγράμματα, περιέχει την πολύ γνωστή `main`. Αυτή είναι η πρώτη συνάρτηση που καλείται, και πρέπει οπωσδήποτε να υπάρχει, είναι *int*,
γιατί πρέπει να επιστρέφει μια τιμή τύπου int στο *shell* (το γνωστό σε όλους μας **exit status**).
----------------------------------------------------------------------------------------------------------------------------------------------------------------
int main(int argc, char *argv[]){
...
...
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Μέσα σε αυτήν τώρα, πρέπει να υπάρχει μια άλλη, η `gtk_init` που καλείται από όλα τα προγράμματα που είναι γραμμένα σε gtk για την αρχικοποίηση.
----------------------------------------------------------------------------------------------------------------------------------------------------------------
gtk_init(&argc, &argv);
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Επίσης, σε κάποιο σημείο πρέπει να καλείται η `gtk_main` η οποία δεν είναι τίποτα άλλο από την συνάρτηση η οποία περιμένει για ενέργειες του χρήστη, όπως το
πάτημα ενός κουμπιού στο ποντίκι, ή το πάτημα ενός πλήκτρου.
----------------------------------------------------------------------------------------------------------------------------------------------------------------
gtk_main();
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Φυσικά δεν πρέπει να ξεχάσουμε την τιμή που θα επιστρέφει το όλο πρόγραμμά μας (είναι είπαμε *int*), και αυτό γίνεται με την γνωστή `return`
----------------------------------------------------------------------------------------------------------------------------------------------------------------
return(0);
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Αυτά είναι τα βασικά και απαραίτητα που πρέπει να περιέχει ένα πρόγραμμα σε gtk. Φυσικά, για να γραφεί ένα ολοκληρωμένο και λειτουργικό πρόγραμμα απαιτούνται
πολύ περισσότερα που θα δούμε στη συνέχεια, γιαύτο μη βιάζεστε\... Συνεχίστε το διάβασμα για την βουτά στα βαθιά\... )))
### [4.2 widgets, signals, κ.λπ.]{#ss4.2}
First things first\...
#### Widgets
Πριν αρχίσουμε, να δούμε μερικά πράγματα, όπως πχ. τι είναι το gtk\_widget που θα συναντάμε κατ κόρον. Είναι μια δομή, που μέσω αυτής μπορούμε να έχουμε
πρόσβαση σε όλα τα widgets της gtk. Αυτά μπορεί να είναι **buttons, radio buttons, check buttons, lists, combo boxes, boxes, toolbars**, και γενικότερα
οτιδήποτε βλέπετε στα παραθυράκια των προγραμμάτων gtk.
#### signals
Ο έλεγχος σε ένα πρόγραμμα **gtk** δίδεται χρησιμοποιώντας τα **signals**. Ας σας εξηγήσω όμως με ένα παράδειγμα.\
Για να συνδέσουμε ένα συμβάν με μια λειτουργία, μπορούμε να χρησιμοποιούμε μια συνάρτηση όπως η `gtk_signal_connect`.\
Αυτή, συντάσσεται όπως βλέπουμε παρακάτω, και επιστρέφει μια τιμή τύπου **gint** (Μια μορφή ακεραίου που χρησιμοποιεί η gtk).
----------------------------------------------------------------------------------------------------------------------------------------------------------------
gint gtk_signal_connect(GtkObject *object, gchar name, GtkSignalFunc func, gpointer func_data);
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Παρακάτω δίνονται οι απαραίτητες εξηγήσεις για να καταλάβετε τι κάνει το κάθε όρισμα:
- `GtkObject *object`\
Το widget που θα παρακολουθούμε για signal
- `gchar name`\
Το signal για το οποίο παρακολουθούμε
- `GtkSignalFunc func`\
Η συνάρτηση που θα κληθεί όταν γίνει trap στο σινιάλο που παρακολουθούμε
- `gpointer func_data`\
Το όρισμα που θα περάσει στην καλούμενη συνάρτηση (μπορεί να είναι πχ, το πλήκτρο που πατήθηκε)
Το τρίτο όρισμα, είναι μια συνάρτηση που δέχεται σαν ορίσματα ένα δείκτη (pointer) στο widget από το οποίο προκλήθηκε το signal, και ένα δείκτη που αναφέρεται
στο τέταρτο όρισμα της καλούσας συνάρτησης (το func\_data δηλαδή). ώστε να ξέρει τι να κάνει με τα δεδομένα που της εστάλησαν (ΜΠΕΡΔΕΥΤΗΚΑΤΕ;)
### [4.3 Γεια σας μάγκες! Το πρώτο πρόγραμμα σε gtk]{#ss4.3}
Επειδή ήδη αρχίσαμε να κολυμπάμε σε βαθύτερα νερά, ας φτιάξουμε ένα μικρό προγραμματάκι, και ας το εξηγήσουμε στη συνέχεια. Κάντε copy-paste, σε ένα αρχείο το
παρακάτω:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <gtk/gtk.h>
void hello(GtkWidget *widget, gpointer data){
g_print("Hello Magez!");
}
gint del_eve(GtkWidget *widget, GdkEvent *event, gpointer data){
g_print("close pressed\n");
return(TRUE);
}
void dest(GtkWidget *widget, gpointer data){
gtk_main_quit();
}
int main(int argc, char *argv[]){
GtkWidget *window, *button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(del_eve), NULL);
gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(dest), NULL);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
button = gtk_button_new_with_label("Hello Magez");
gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(hello), NULL);
gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window));
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show(button);
gtk_widget_show(window);
gtk_main();
return(0);
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Ονομάστε το αρχείο HelloMagez.c και αποθηκεύστε το κάπου που έχετε δικαιώματα (πχ. στο home directory σας). Παρακάτω θα δούμε πως μπορούμε να δημιουργήσουμε το
εκτελέσιμο αρχείο.
### [4.4 Εξήγηση του Hello Magez]{#ss4.4}
Ας δούμε τώρα πως δουλεύει. Θα εξηγούμε το πρόγραμμα με μικρά βήματα. Αλλά ας μην αρχίσουμε από την αρχή, πάμε κατευθείαν στην main.
- **`GtkWidget *window, *button;`**\
Εδώ ορίζουμε ότι θα χρησιμοποιήσουμε δύο widgets, με ονόματα `window` και `button`
- **`gtk_init(&argc, &argv);`**\
Η γνωστή **gtk\_init**. Την αναφέραμε προηγουμένως.
- **`window = gtk_window_new(GTK_WINDOW_TOPLEVEL);`**\
Ορισμός στο widget **window** ενός νέου παραθύρου τύπου **`GTK_WINDOW_TOPLEVEL`**, δηλαδή κανονικού παραθύρου προγράμματος.
- **`gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(del_eve), NULL);`**\
Εδώ αρχίζουν τα δύσκολα. Μη φοβηθείτε όμως, θα τα εξηγήσουμε όλα. Αυτό που κάνουμε, είναι να συνδέσουμε το συμβάν `delete_event` με την συνάρτηση `del_eve`.
To `delete_event` το στέλνει ο window manager που χρησιμοποιούμε όταν πατήσουμε το **close**, ή αντίστοιχα το κουμπί **close** στην **bar** του
προγράμματος. Γιατί το παγιδεύουμε αυτό; Μα γιατί ίσως να θέλουμε να κάνουμε κάποιες εργασίες πριν να κλείσουμε το παράθυρο, πχ. να αποθηκεύσουμε ένα αρχείο
ρυθμίσεων, ή να εμφανίσουμε ένα μήνυμα του τύπου \`\`Είστε σίγουρος;\'\'\
To `GTK_OBJECT` και το `GTK_SIGNAL_FUNC` είναι ουσιαστικά μακροεντολές, που ελέγχουν αν είναι σωστές οι παράμετροι που περνάμε στην `gtk_signal_connect` και
(σύμφωνα με μερικούς-μερικούς) βοηθάει να είναι ο κώδικας πιο ευανάγνωστος και πιο κατανοητός.
- **`gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(dest), NULL);`**\
\`Aλλη μια σύνδεση. Σε αυτή συνδέουμε το συμβάν `destroy` με τη συνάρτηση `dest`. Το συμβάν `destroy` συμβαίνει όταν δίνουμε στο `delete_event` την τιμή
FALSE, ή όταν καλούμε το `gtk_widget_destroy()` που είναι μια συνάρτηση στην οποία περνάμε σαν παράμετρο το όνομα του παραθύρου που θέλουμε να καταστρέψουμε
(κλείσουμε). Με αυτό τον τρόπο, με μια συνάρτηση, ελέγχουμε και τις δύο περιπτώσεις.
- **`gtk_container_set_border_width(GTK_CONTAINER(window), 10);`**\
Αυτή η εντολή, απλά θέτει μια ιδιότητα για ένα αντικείμενο. Συγκεκριμένα, την ιδιότητα *border* στο widget `window`, που είναι ο χώρος γύρω από το παράθυρο
που μένει ανεκμετάλλευτος και δεν μπορεί να χρησιμοποιηθεί από άλλα widgets. Αυτό το κάνουμε για αισθητικούς λόγους.\
Το `GTK_CONTAINER` είναι και αυτό μια μακροεντολή, για type casting, όπως τα `GTK_OBJECT` και `GTK_SIGNAL_FUNC`.
- **`button = gtk_button_new_with_label("Hello Magez");`**\
Συνάρτηση για τη δημιουργία ενός κουμπιού που γράφει \"Hello Magez\". Φυσικά, κουμπιά μπορούν να δημιουργηθούν και αλλιώς, χωρίς να είμαστε αναγκασμένοι να
τους δώσουμε ένα κείμενο, σε περίπτωση πχ. που το κείμενο θα εξαρτάται από κάποιες μεταβλητές, ή θα πρέπει να αλλάξει μετά από λίγο.
- **`gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(hello), NULL);`**\
Συνδέουμε το click στο κουμπί, με τη συνάρτηση `hello`. Αυτό είναι πολύ απλό, και εύκολο στην κατανόηση.
- **`gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window));`**\
Να και κάτι καινούριο. Με αυτή την εντολή, παρατηρούμε ότι μπορούμε να συνδέσουμε πολλές συναρτήσεις με ένα event (στην περίπτωση αυτή με το click του
ποντικιού στο κουμπί `button`. Αυτό που θα συμβεί είναι ότι πρώτα θα καλέσουμε τη συνάρτηση `hello` και αμέσως μετά τη συνάρτηση `gtk_widget_destroy` που
είναι μια συνάρτηση που \`\`καταστρέφει\'\' το widget που της δίνεται σαν όρισμα, στη συγκεκριμένη περίπτωση το `window`, που είναι το παράθυρο του
προγράμματός μας.\
Θα παρατηρήσατε ότι εδώ χρησιμοποιείται η `gtk_signal_connect_object` αντί της `gtk_signal_connect`. Αυτό συμβαίνει γιατί πρέπει να περάσουμε σαν όρισμα το
widget που πρέπει να καταστραφεί. Περισσότερα για τα signals αργότερα.
- **`gtk_container_add(GTK_CONTAINER(window), button);`**\
Κι άλλα καινούρια! η `gtk_container_add` είναι μια συνάρτηση που προσθέτει ένα widget σε ένα container. Εδώ, widget=`button` και container=`window`.\
Σημειώστε ότι ένα `gtk_container` μπορεί να περιέχει μόνο ένα widget. Υπάρχουν όμως άλλα widgets, που πάνω τους μπορούν να φιλοξενούν πολλά άλλα widgets.
Αυτό, στην ενότητα των widgets θα αναλυθεί πολύ καλύτερα. Προς το παρών αρκεστείτε σε αυτό, και τυχόν απορίες σας θα λυθούν παρακάτω.
- **`gtk_widget_show(button);`**\
Αυτή η συνάρτηση εμφανίζει το widget που δέχεται σαν όρισμα. Παρατηρείστε ότι τα widgets, δεν εμφανίζονται μόνα τους. Δεν φτάνει δηλαδή η συνάρτηση
`gtk_container_add` για να εμφανιστεί. Πρέπει πρώτα να προστεθούν όλα, και μετά να εμφανιστούν. Αυτό, μας προστατεύει από συμπεριφορές του τύπου να γίνονται
όλα render στην οθόνη ένα-ένα το οποίο οπτικά είναι πολύ άσχημο, πιστέψτε με. Εμφανίζουμε λοιπόν με την συγκεκριμένη εντολή το κουμπάκι.
- **`gtk_widget_show(window);`**\
\`Οπως και το παραπάνω, μόνο που εδώ εμφανίζουμε το παράθυρο.
- **`gtk_main();`**\
Περνάμε τον έλεγχο στην `gtk_main` όπως περιγράψαμε προηγουμένως.
- **`retutn(0);`**\
Είναι το **exit status** του προγράμματός μας. Θα μπορούσαμε με κάποιο έλεγχο να είχαμε διαφορετικό **exit status**, ανάλογα με το αν το πρόγραμμα απέτυχε,
αν το πρόγραμμα δεν ολοκληρώθηκε, κ.λπ.
Και ας ρίξουμε μια γρήγορη ματιά στις συναρτήσεις που περιέχει το πρόγραμμα. Αν και είναι πολύ εύκολες στην κατανόηση, υπάρχουν κάνα-δυο σημεία που χρειάζονται
εξήγηση.
- **`void hello(GtkWidget *widget, gpointer data)`**\
\`Οπως βλέπουμε, είναι μια συνάρτηση που απλά καλείται όταν πατήσει ο χρήστης το πλήκτρο, και το μόνο που κάνει είναι να τυπώνει **Hello Magez** και να
προσθέτει μια νέα γραμμή.
- **`gint del_eve(GtkWidget *widget, GdkEvent *event, gpointer data)`**\
Η συνάρτηση που καλείται όταν πατηθεί το κουμπί που τερματίζει το πρόγραμμα. Επιστρέφει τιμή ακεραίου, και όπως βλέπουμε (στο συγκεκριμένο παράδειγμα
τουλάχιστον) επιστρέφει TRUE. Αυτός είναι και ο λόγος που δεν έχουμε έξοδο από το πρόγραμμα όταν -θεωρητικά- το κλείνουμε με το κουμπί κλεισίματος. Αν η
τιμή που επιστρέφει αλλαχθεί σε FALSE, τότε θα έχουμε έξοδο από το πρόγραμμα (δοκιμάστε το). Αυτό συμβαίνει, γιατί η συνάρτηση που καλείται από την
`delete_event` εξ ορισμού από τo gtk επιστρέφει μια τιμή που αν είναι FALSE, καλεί το event `destroy` (θυμηθείτε ότι το έχουμε συνδέσει με τη συνάρτηση
`dest`)
- **`void dest(GtkWidget *widget, gpointer data)`**\
Με τη συνάρτηση αυτή έχουμε έξοδο από το πρόγραμμα, μέσω μιας ενσωματωμένης στο gtk συνάρτησης, της `gtk_main_quit`
### [4.5 Πως θα το τρέξετε.]{#ss4.5}
Για να κάνετε compile to πρόγραμμα, χρησιμοποιήστε την εντολή:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
gcc -Wall -g HelloMagez.c -o hellomagez `gtk-config --cflags` `gtk-config --libs`
----------------------------------------------------------------------------------------------------------------------------------------------------------------
\`Οσοι δεν ξέρουν τι είναι αυτό, **man gcc**. Μόλις ολοκληρωθεί η παραπάνω εντολή, στο τρέχον directory θα έχει δημιουργηθεί το αρχείο **hellomagez**, που είναι
και το εκτελέσιμο. Τρέξτε το!
### [5. Tοποθέτηση των widgets]{#s5}
Το πιο πιθανό είναι να μην δημιουργήσουμε ποτέ μια εφαρμογή που θα έχει μονάχα ένα *widget*, όπως στην περίπτωση του **HelloMagez**. Μάλλον, αυτό που θα θέλουμε
να κάνουμε θα αποτελείται από περισσότερα από ένα *widget*. Ας δούμε πως μπορούμε να τα τοποθετήσουμε στο παράθυρο του προγράμματός μας.
### [5.1 Boxes, ή αλλιώς κουτιά :)]{#ss5.1}
Τα *boxes* θα γίνουν οι καλύτεροι φίλοι μας στη διαδικασία σύνταξης εφαρμογών για **gtk**, αλλά - πιστέψτε με - και οι χειρότεροί μας εχθροί. Θα μάθουμε να
ζούμε με τα *boxes*, και να είστε σίγουροι ότι δεν θα τα γλιτώσουμε. Τι είναι όμως αυτά τα *boxes*;\
Είναι widgets, όπως και όλα τα άλλα. Απλά, μπορούν να περιέχουν περισσότερα από 1 widgets πάνω τους. Δεν είναι ορατά, και χρησιμοποιούνται μονάχα για την
ομαδοποίηση αντικειμένων. Τα boxes μπορεί να είναι δύο ειδών, ανάλογα με τον προσανατολισμό που θέλουμε να πάρουν τα προς τοποθέτηση σε αυτό *widgets*:
- **HORIZONTAL BOXES**\
Τα τοποθετημένα σε αυτό *widgets* θα μπουν στη σειρά από αριστερά προς τα δεξιά ή από τα δεξιά προς τα αριστερά όπως θα δούμε παρακάτω στην εξήγηση των
συναρτήσεων **`gtk_box_pack_start`** και **`gtk_box_pack_end`**.\
H δημιουργία ενός τέτοιου *box* επιτυγχάνεται με τη χρήση της συνάρτησης **`gtk_hbox_new`**.
- **VERTICAL BOXES**\
Τα τοποθετημένα σε αυτό *widgets* θα μπουν στη σειρά από πάνω προς τα κάτω ή από κάτω προς τα πάνω, πάλι με χρήση των **`gtk_box_pack_start`** και
**`gtk_box_pack_end`**.\
Σε γενικές γραμμές, η δημιουργία ενός τέτοιου *box* επιτυγχάνεται με τη χρήση της συνάρτησης **`gtk_hbox_new`**.
#### Δημιουργία, σύνταξη και παραμέτροι.
Για τη δημιουργία ενός box, χρησιμοποιούμε τις **`gtk_hbox_new`** και **`gtk_vbox_new`** όπως αναφέραμε και παραπάνω.\
Η σύνταξη και ο τύπος των συναρτήσεων αυτών είναι:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
GtkWidget *gtk_hbox_new(gint homogeneous, gint spacing);
GtkWidget *gtk_vbox_new(gint homogeneous, gint spacing);
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Στη συνέχεια θα αναφέρομαι μονάχα στην εντολή `gtk_hbox_new` και ότι ισχύει για αυτήν ισχύει και για την `gtk_vbox_new`. \`Οπως παρατηρούμε, επιστρέφει τιμή
τύπου `GtkWidget`, και παίρνει δύο τιμές, τύπου *gint*, τις **homogenous** και **spacing**.
Η **homogenous** μπορεί να είναι TRUE ή FALSE (και φυσικά οποιαδήποτε λογική έκφραση) και μας δίνει τη δυνατότητα να ελέγξουμε αν τα *widgets* που θα
τοποθετηθούν στο box θα έχουν το ίδιο πλάτος όλα ή όσο χρειάζεται το κάθε ένα.
Η παράμετρος **spacing** καθορίζει πόσα pixels θα απέχουν μεταξύ τους τα *widgets*.
#### Εργασία με τα boxes
Δεν πιστεύω να σας τρόμαξα με τα όσα έγραψα για τα boxes στην προηγούμενη ενότητα\... \`Οπως θα δείτε, η εργασία με τα boxes είναι μια εύκολη υπόθεση, αν ξέρετε
να δουλεύετε τις συναρτήσεις **`gtk_box_pack_start`** και **`gtk_box_pack_end`**. Στη συνέχεια θα δούμε πόσο εύκολη είναι η εργασία με αυτές τις συναρτήσεις.
**`gtk_box_pack_start`**\
**`gtk_box_pack_end`**
Η μορφή αυτών των συναρτήσεων είναι:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
void gtk_box_pack_start(GtkBox *box, GtkWidget *child, gint expand, gint fill, gint padding );
void gtk_box_pack_end(GtkBox *box, GtkWidget *child, gint expand, gint fill, gint padding );
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Δεν επιστρέφουν καμία τιμή (είναι void απ\' ότι βλέπετε) και τα ορίσματα που δέχονται εξηγούνται παρακάτω:
- **`GtkBox *box`**\
Το όνομα του *box* στο οποίο αναφερόμαστε.
- **`GtkWidget *child`**\
Το όνομα του *widget* το οποίο προσθέτουμε.
- **`gint expand`**\
Είναι μορφής *gint*, και αυτό που περιγράφει είναι αν το *widget* που τοποθετούμε θα πιάνει όλη την περιοχή του *box*. Μπορούμε να του αποδώσουμε τις τιμές
TRUE και FALSE. TRUE στην περίπτωση που θέλουμε να πιάνει όλη την διαθέσιμη περιοχή, και FALSE αν θέλουμε να πιάνει μόνο όση περιοχή του είναι απαραίτητη.
Δίδοντας την τιμή FALSE μπορούμε να επιτύχουμε (αριστερή στην περίπτωση της **`gtk_box_pack_start`** ή δεξιά στην περίπτωση της **`gtk_box_pack_end`**)
στοίχιση.\
\`Οπως καταλαβαίνετε, αν δοθεί η τιμή TRUE, δεν έχει σημασία πια από τις **`gtk_box_pack_start`** ή **`gtk_box_pack_end`** χρησιμοποιήσουμε.
- **`gint fill`**\
Η τιμή αυτής της παραμέτρου έχει σημασία μόνο αν η τιμή της **`expand`** είναι TRUE. Και αυτό γιατί περιγράφει αν ο επιπλέον χώρος που μένει μετά τη
δημιουργία των *widgets* θα ανήκει σε αυτά (Δηλ. θα τα μεγαλώσει) στην περίπτωση που πάρει την τιμή TRUE ή αν θα ανήκει στο *box* σαν αδιάθετος χώρος στην
περίπτωση που της δώσουμε την τιμή FALSE
- **`gint padding`**\
Αριθμός τύπου *gint*, που καθορίζει πόσος χώρος (σε pixels) γύρω από το κάθε *widget* δεν θα χρησιμοποιείται.
### [5.2 Tοποθέτηση των widgets, partII (λέγε με tables)]{#ss5.2}
Υπάρχει και άλλος ένας τρόπος για να στοιχίσουμε τα widgets στο παράθυρο της εφαρμογής μας. Αυτός επιτυγχάνεται με τη χρήση της συνάρτησης **`gtk_table_new`**
που δημιουργεί έναν πίνακα στο παράθυρο της εφαρμογής μας. Φυσικά, και αυτή η συνάρτηση χρησιμοποιείται μόνο για την τοποθέτηση των διαφόρων *widgets* στο
παράθυρο, και δεν σχεδιάζεται κανένας πίνακας στην εφαρμογή μας.\
Η σύνταξή της είναι η εξής:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
GtkWidget *gtk_table_new(gint rows, gint columns, gint homogeneous );
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Είναι προφανές ότι rows είναι ο αριθμός των γραμμών του πίνακά μας και columns ο αριθμός των στηλών του.
Το homogenous περιγράφει τον τρόπο που τα κελιά του πίνακα θα διανέμονται και θα τοποθετούνται πάνω στο παράθυρό μας. Αν είναι TRUE, τότε όλα τα κελιά του
πίνακα μεγαλώνουν ή μικραίνουν, σύμφωνα με το μεγαλύτερο *widget* στον πίνακα. Αν πάρει την τιμή FALSE, το μέγεθος ορίζεται από το πιο μακρύ *widget* στην κάθε
στήλη και το πιο ψηλό *widget* στην σειρά. Αυτό που ουσιαστικά συμβαίνει είναι ότι αν το HOMOGENEOUS είναι TRUE, όλα τα *widgets* έχουν το ίδιο μέγεθος, και
είναι ομοιόμορφα τοποθετημένα πάνω στο box.
Η διάταξη των κελιών του πίνακα, διαμορφώνεται από το 0 μέχρι τις τιμές *rows* και *columns*. Μoιάζει δηλαδή με το παρακάτω. Η αρίθμηση όπως βλέπετε, ξεκινάει
από την πάνω αριστερή γωνία.
----------------------------------------------------------------------------------------------------------------------------------------------------------------
0 1 2
0+----------+----------+
| | |
1+----------+----------+
| | |
2+----------+----------+
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Για την τοποθέτηση ενός widget σε ένα πίνακα, χρησιμοποιούμε την συνάρτηση **`gtk_table_attach`**, της οποίας η σύνταξη είναι η παρακάτω
----------------------------------------------------------------------------------------------------------------------------------------------------------------
void gtk_table_attach(GtkTable *table, GtkWidget *child, gint left_attach, gint right_attach,
gint top_attach, gint bottom_attach, gint xoptions,
gint yoptions, gint xpadding, gint ypadding);
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Δεν επιστρέφei καμία τιμή και τα ορίσματα που δέχεται είναι
- **`GtkTable *table`**\
Το Table στο οποίο αναφερόμαστε
- **`GtkWidget *child`**\
Το widget που θέλουμε να τοποθετήσουμε
- **`gint left_attach`**\
Η αριστερή συντεταγμένη από όπου θα αρχίζει το widget
- **`gint right_attach`**\
Η δεξιά συντεταγμένη από όπου θα τελειώνει το widget
- **`gint top_attach`**\
Η πάνω συντεταγμένη από όπου θα αρχίζει το widget
- **`gint bottom_attach`**\
Η κάτω συντεταγμένη που θα τελειώνει το widget
- **`gint xoptions`**\
χρησιμοποιείται για τον καθορισμό του τρόπου πακεταρίσματος, και θα το δούμε στη συνέχεια πιο αναλυτικά
- **`gint yoptions`**\
\`Οπως και το παραπάνω, και οι τιμές που μπορεί να πάρει τόσο αυτό όσο και το `xoptions` είναι οι παρακάτω:
- **`GTK_FILL`**\
Αν η περιοχή (συνήθως το κουτί του πίνακα) είναι μεγαλύτερο από το widget που τοποθετούμε, το widget θα μεγαλώσει τόσο ώστε να καλύψει όλο τον χώρο που
υπάρχει.
- **`GTK_SHRINK`**\
Αν κατά την απόδοση χώρου σε ένα κουτί ο χώρος που δίνεται σε ένα widget είναι μικρότερος από το μέγεθός του ίδιου του widget, τότε αυτό θα μικράνει
ώστε να χωράει. Κάτι τέτοιο συμβαίνει όταν ο χρήστης κάνει resize σε ένα παράθυρο. Αν δεν είναι ορισμένο το **`GTK_SHRINK`** είναι πολύ πιθανό σε μια
τέτοια περίπτωση να μην εμφανίζονται τα widgets μέσα στον χώρο του παραθύρου μας.
- **`GTK_EXPAND`**\
Με αυτό τον τρόπο μπορούμε να αποδώσουμε στο table μας όλο τον χώρο που απομένει στο παράθυρο μετά τη δημιουργία του.
Οι παραπάνω τιμές που μπορούν να πάρουν, είναι δυνατό να συνδυαστούν με το **OR** για να καλύψουμε περισσότερες από μια περιπτώσεις.
- **`gint xpadding`**\
Το γνωστό και πολλάκις εξηγημένο padding στον οριζόντιο άξονα
- **`gint ypadding`**\
\`Οτι και το παραπάνω, αναφέρεται όμως στον κατακόρυφο άξονα
Για όλους εσάς που όλα αυτά τα ορίσματα στην παραπάνω συνάρτηση σας φάνηκαν πολλά, υπάρχει και μια άλλη, που κάνει την ίδια (περίπου) δουλειά απλούστερα.
----------------------------------------------------------------------------------------------------------------------------------------------------------------
void gtk_table_attach_defaults(GtkTable *table, GtkWidget *widget, gint left_attach,
gint right_attach, gint top_attach, gint bottom_attach);
----------------------------------------------------------------------------------------------------------------------------------------------------------------
\`Οπως παρατηρούμε, μόνο οι βασικές παραμέτροι περνάνε στη συνάρτηση. Ουσιαστικά δηλαδή, μόνο τα controls θέσης και μεγέθους. Οι παραμέτροι που δεν αναφέρονται,
παίρνουν κάποιες προκαθορισμένες τιμές. Αυτές είναι οι πλέον χρησιμοποιούμενες, και συγκεκριμένα τα **X** και **Y options** γίνονται
**`GTK_FILL | GTK_EXPAND`**, (ελπίζω να ξέρετε ότι το \`\`\|\'\' είναι το **OR**) και στα **X** και **Y padding** δίδεται η τιμή
0.
Για να καθορίσουμε το spacing ανάμεσα σε συγκεκριμένες γραμμές και στήλες ενός πίνακα, χρησιμοποιούμε τις παρακάτω συναρτήσεις:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
void gtk_table_set_row_spacing(GtkTable *table, gint row, gint spacing);
void gtk_table_set_col_spacing(GtkTable *table, gint col, gint spacing);
----------------------------------------------------------------------------------------------------------------------------------------------------------------
όπου row ή col η γραμμή ή η στήλη στην οποία αναφερόμαστε, και spacing η απόσταση που θέλουμε να ορίσουμε σαν spacing. Για τις στήλες το spacing προστίθεται στα
ΔΕΞΙΑ και για τις γραμμές ΚΑΤΩ.
Είναι επίσης δυνατό να καθορίσουμε για ΟΛΕΣ τις στήλες ή γραμμές το spacing, χρησιμοποιώντας τις συναρτήσεις:
----------------------------------------------------------------------------------------------------------------------------------------------------------------
void gtk_table_set_row_spacings(GtkTable *table, gint spacing);
void gtk_table_set_col_spacings(GtkTable *table, gint spacing);
----------------------------------------------------------------------------------------------------------------------------------------------------------------
\`Οπως πολύ καλά καταλάβατε, η τελευταία στήλη και η τελευταία σειρά δεν λαμβάνουν αυτή την τιμή, γιατί θα δημιουργούσε ένα κενό στα αριστερά και ένα κενό κάτω.
Είναι φανερό ότι το να δουλεύει κανείς με tables είναι εύκολο. Εγώ προσωπικά δεν το προτιμώ, παρόλο που είναι βασικά η μόνη λύση στην περίπτωση που θέλουμε
απόλυτα στοιχισμένα κουτάκια και κουμπάκια και widgets. Δοκιμάστε το πάντως, και πολλές φορές θα σας λύσει τα χέρια
### [5.3 Η συνέχεια]{#ss5.3}
Τον επόμενο μήνα θα συνεχίσουμε αυτό το άρθρο και θα ασχοληθούμε περισσότερο με τα widgets (buttons, radio/check/toggle buttons, text boxes, κ.λπ.) και τις
ιδιότητές τους. Μέχρι τότε, πολλά φιλάκια.

69
content/articles/20/05_civ.md Κανονικό αρχείο

@ -0,0 +1,69 @@
+++
title = 'Civ Call To Power'
date = '1999-12-01T00:00:00Z'
description = ''
author = 'Αντώνης Μαυρέλος(mailto:A.Mavrelos@csd.ase.gr)'
issue = ['Magaz 20']
issue_weight = 5
+++
----------------------------------------------------------------------------------------------------------------------------------------------------------------
*Μία παρουσίαση του παιχνιδιού Civ Call To Power που κυκλοφόρησε για Linux από την Loki*
----------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Τίτλος: Civ Call To Power
Κατασκευαστής: Activision
Μεταφορά στο Linux: Loki Entertainment Software
Διανομή στην Ελλάδα: SuSe
Τιμή: 16000 (Αν βρείτε τον Νικήτα στο μαγαζί του Παπασωτηρίου)
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Για μένα το Civilization είναι ένα από τα καλύτερα παιχνίδια όλων των εποχών. Η πρώτη του έκδοση κυκλοφόρησε στις αρχές της δεκαετίας για DOS μόνο τότε. Μετά
βγήκε και η έκδοση για Amiga και μπορούσα να ξενυχτήσω και εγώ όπως οι άλλοι τυχεροί. Το Civilization διέθετε ένα χαρακτηριστικό το οποίο δεν είχαν τα άλλα
παιχνίδια. Ηταν ρεαλιστικό. Το θέμα του είχε άμεση σχέση με την πορεία του ανθρώπου κατά την διάρκεια της εξέλιξης του τόσο πολιτισμικά όσο και κυριαρχικά. Δεν
μιλούσε για Θεούς και Δαίμονες, ήρωες, πολεμιστές, φανταστικά γεγονότα και ιστοριούλες. Αναλάμβανες να οδηγήσεις ένα πολιτισμό από το -3000 π.Χ μέχρι το 2020
μ.Χ. Να ανακαλύψεις ό,τι ανακάλυψε ο άνθρωπος κατά τη διάρκεια του χρόνου. Αντίπαλοι σου άλλες φυλές όλες οδηγούμενες από τον Υπολογιστή.
Η έκδοση 2 κυκλοφόρησε με βελτιωμένα γραφικά και για την πλατφόρμα των Windows. Το θέμα δεν άλλαξε, διορθώθηκαν μικροατέλειες και η ΑΙ. Το 2 πούλησε ακόμα
περισσότερο και χαρακτηρίστικε από πολλά \"δημοψηφίσματα\" σαν το καλύτερο παιχνίδι όλων των εποχών. Φυσικό ήταν λοιπόν και η όποια συνέχειά του να ακολουθήσει
την ίδια συνταγή. Ο Sid έφυγε από την Microprose που ήταν και η κατασκευάστρια εταιρεία και δημιούργησε την Firaxis. Η Firaxis έβγαλε πριν ένα χρόνο το Alpha
Centauri ταυτόχρονα με την Activision που έβγαλε για τα Windows το Civ. Τα δύο παιχνίδια έχουν το ίδιο θέμα αλλά το ένα χρονικά βρίσκεται στο μέλλον και είναι η
συνέχεια του αρχικού Civilization που τελείωνε όταν ο άνθρωπος έφτανε στο Αλφα του Κεντάυρου και το δεύτερο\...
Ενα βελτιωμένο από όλες τις απόψεις Civilization. Καταρχήν Multiplayer. Εστω και Turn Based Strategy (όχι Real Time) έχει μεγαλύτερο ενδιαφέρον να παίζεις
εναντίον ανθρώπου παρά του υπολογιστή. Τα γραφικά είναι καταπληκτικά, παίζει σε παράθυρο αλλά και με DGA σε Full Screen. Υπάρχουν σκηνές μάχης, με αντιπαράθεση
των στρατών ο ένας απέναντι από τον άλλο. Τα σενάρια είναι πιο πολύπλοκα και, γιατί όχι, έξυπνα.
Για παράδειγμα υπάρχει δυνατότητα να πιάσεις δούλους από γειτονικές πόλεις, να χρησιμοποιήσεις δικηγόρους(!), παπάδες και άλλα τέτοια τρελά πράγματα. Ολα αυτά
δίνουν στο gameplay μια ώθηση.
Ενα ενοχλητικό χαρακτηριστικό είναι κάποια buttons που βρίσκονται στο κάτω μέρος χαμηλά στην οθόνη και αρκετές φορές παρεμβαίνει το Panel του KDE όταν τα
πατάμε.
O έλεγχος για όποιον έχει συνηθήσει στα προηγούμενα είναι αρκετά δύσκολος. Η βοήθεια που σου δίνουν ta icons στο αριστερό μέρος είναι μεγάλη αρκετές φορές (για
παράδειγμα ξεχνιέσαι να δώσεις δουλειά σε μια πόλη ή αφήνεις να φτιάχνει το ίδιο πράγμα πολλές φορές). Είναι όμως ενοχλητικά τα πολλά κλικ και γίνονται αρκετά
\"χιλιόμετρα\" με το ποντίκι.
Το παιχνίδι θα φανεί τις πρώτες φορές εχθρικό. Γίνονται πολλές ξαφνικές επιθέσεις από βάρβαρους ή από άλλες φυλές αλλά σε γενικές γραμμές είναι εύκολο.
![](/20/img/civ_sm.png)\
[Μεγάλο μέγεθος](/20/img/civ.png)
**Installation**
To πακέτο περιέχει 2 δισκάκια. Στο ένα είναι το παιχνίδι και στο άλλο το SuSE 6.1. Φανταστείτε στην έκδοση για Windows να είχε άλλο ένα δισκάκι με τα Windows
98. Αν πάει καλά το Linux και μειωθεί το ποσοστό των windows users μπορεί η MS σε 3-4 χρόνια να δίνει μια Lite έκδοσή τους ίσα-ίσα για να ξεκινήσει το παιχνίδι.
Οι οδηγίες εγκατάστασης είναι απαράδεκτες. To πρόγραμμα που κάνει install τα tar.gz files δεν είναι καν executable. Δεν υπάρχει πρόβλημα για κάποιον που έχει
λίγες απλές γνώσεις αλλά για τον αρχάριο είναι βουνό. Σίγουρα τα επόμενα παιχνίδια δεν θα έχουν τέτοιες μικροατέλειες. H Loki έχει βγάλει τον Installer σε open
source και ήδη βοηθάει κόσμος. Ας θυμηθούμε πώς γινόταν η εγκατάσταση στις πρώτες εκδόσεις του Linux και που φτάσαμε σήμερα με την Corel και Caldera.
Κατά τα άλλα το εγχειρίδιο του παιχνιδιού είναι άριστο. Στις 100 σελίδες του βρίσκονται όλα όσα χρειάζεται κανείς και είναι ευκολοδιάβαστο. Δεν υπάρχουν πολλά
Hints αλλά σίγουρα μετά από 1-2 χρόνια θα βγει κάποια Special Edition με ένα ακόμα πιο πλήρες manual. Υπάρχει και μια αφίσσα που θα κοσμήσει τον τοίχο σας.
Το κόστος του Civ είναι αρκετά μεγάλο. Στις 16900 κρίνεται εξαρχής απαγορευτικό για κάποιους, ενώ για κάποιους άλλους η κόπια θεωρείται μονόδρομος. Πιστεύω ότι
με το καιρό και όσο πολλαπλασιάζονται οι εγκαταστάσεις με Linux θα μειωθούν οι τιμές. Civ στο Linux ποιος θα το φανταζοταν πριν 2 χρόνια! Να περιμένουμε δηλαδή
και την Enacarta 2001;