Synthé-modulaireConnexion

Quelques lamas, sur la cordillère des ondes


Partagez

descriptionQu'est ce ...

more_horiz
Si ici il y a des codeurs qui aiment l'archéologie... logiciel
Crazy

Code:

/*
 *                      Copyright (c) 1993 by IRCAM
 *                          All rights reserved.
 *
 *  For any information regarding this and other IRCAM software, please
 *  send email to:
 *                              [Vous devez être inscrit et connecté pour voir ce lien]
 *
 *      $Revision: 1.1.1.1 $ IRCAM $Date: 1997/07/10 14:24:58 $
 *
 * FTS by Miller Puckette
 *
 */

/* This is the main routine for MAX. */

#import <appkit/Application.h>
#import <appkit/View.h>
#import <appkit/Menu.h>
#import <appkit/MenuCell.h>
#import <appkit/Window.h>
#import <appkit/Pasteboard.h>
#import <appkit/Listener.h>

#if defined(NEXTSTEP_3) || defined(NEXTSTEP_PC)
#import <defaults/defaults.h>
#else
#import <defaults.h>
#endif

#import <objc/List.h>
#import "mess.h"

typedef void t_wind;

#import "view.h"
#import "charlie.h"
#import <sys/param.h>
#import "fts_max.h"
#import "getvalue.h"
#import "version.h"
#import <string.h>

void *sock_new();

/* Utility for creating paths */

static const char *
makeDefaultFilename(const char *dir, const char *path)
{
  static char buf[1024];

  sprintf(buf, "%s/%s", dir, path);
 
  return buf;
}

static const char *
makeDefaultArchFilename(const char *dir, const char *path, const char *arch)
{
  static char buf[1024];

  sprintf(buf, "%s/%s/%s", dir, path, arch);
 
  return buf;
}


@interface Maxapp : Application      // override the terminate message
{
}
- maxService: (id)pasteboard
    userData:(const char *)userData
      error:(char **)msg;
@end

@implementation Maxapp
- terminate:sender
/*
 * Overridden to be sure all documents get an opportunity to be saved
 * before exiting the program.
 */
{
  int count, choice;
  id window, document;

  count = [windowList count];
  while (count--)
    {
      window = [windowList objectAt:count];
      document = [window delegate];
      if ([document respondsTo:@selector(needsSaving)] &&
     [document needsSaving])
   {
     choice = NXRunAlertPanel("Quit", "You have unsaved documents.",
              "Review Unsaved", "Quit Anyway", "Cancel");
     if (choice == NX_ALERTOTHER)
       {
         return self;
       }
     else if (choice == NX_ALERTDEFAULT)
       {
         count = [windowList count];
         while (count--)
      {
        window = [windowList objectAt:count];
        document = [window delegate];
        if ([document
         respondsTo:@selector(windowWillClose:)])
          {
            if ([document windowWillClose:window])
         {
           [window close];
         }
            else
         {
           return self;
         }
          }
      }
       }
     break;
   }
    }

  [super terminate:sender];

  return nil;
}

extern void *key_keyout_get(), *keyup_keyout_get();

- sendEvent:(NXEvent *)ev
{
  static int didit;
  if (!didit)
    [[NXApp appListener] setServicesDelegate:NXApp];
  didit = 1;

  if (ev->type == NX_KEYDOWN)
    {
      if (key_keyout_get())
   outlet_int(key_keyout_get(), (long)(ev->data.key.charCode));
    }
  else if (ev->type == NX_KEYUP)
    {
      if (keyup_keyout_get())
   outlet_int(keyup_keyout_get(), (long)(ev->data.key.charCode));
    }
  [super sendEvent:ev];
}

- maxService: (id)pasteboard
    userData:(const char *)userData
      error:(char **)msg
{
  const char *types[1];
  char *buffer, *revBuffer, *data;
  int length, i=0, j;

  [pasteboard types];

  if ([pasteboard readType:NXAsciiPboardType data:&data
        length:&length])
    {
      toolbox_sendtocp(data, length);
    }
  else *msg = "Error: Max takes text only";
  return self;
}

@end



#define POLLINTERVAL .1
static DPSTimedEntry timerno;
static float pollinterval = POLLINTERVAL;

/* this is messed up -- it can send back so much graphics the
      NeXT never hands you another event */
   
void h_domaxpoll()
{
  int wascached = post_setcache(1);
  h_dopoll();
  post_setcache(wascached);
}

void max_timefn(void *x, char *buf, int size)
{
  long foo = *(long *)buf;
  restart_settime(foo);
}

void max_startpolling()
{
  timerno = DPSAddTimedEntry(pollinterval, h_domaxpoll, (void *)0, 1);
}

void max_stoppolling()
{
  if (timerno) DPSRemoveTimedEntry(timerno);
  h_setpollfunction(0);
}

h_setpollinterval(f)
    float f;
{
  pollinterval = .05;
}

static argDesc max_argdesc[] = {
  {"help",      "help", 0,      HELP,    BOOL_VALUE,    "Print the usage message and exit"},
  {"version",    "v", "version",      OPTIONAL, BOOL_VALUE,    "Print version and exit"},
  {"NextAteThisOne", 0, 0,      INTERNAL, STRING_VALUE,  ""},
  {"NXOpen",      0,  0,      INTERNAL, STRING_VALUE,  ""},
  {"NXHost",      0,  0,      INTERNAL, STRING_VALUE,  ""},
  {"MachLaunch",  0,  0,      INTERNAL, STRING_VALUE,  ""},
  {"linkType",  "l", "linkType",  OPTIONAL, STRING_VALUE,  "Type of link: sink, socket, ispw, pipe, named_pipe"},
  {"ftsArch",    "arch", "ftsArch",  OPTIONAL, STRING_VALUE,    "fts platform architecture"},
  {"ftsHost",    "h", "ftsHost",    OPTIONAL, STRING_VALUE,  "The remote fts server host"},
  {"patchPath",  "p", "patchPath",  OPTIONAL, STRING_VALUE,  "Search path for patches"},
  {"rootDir",    "r", "rootDir",      OPTIONAL, PATH_VALUE,    "The installation root directory"},
  {"helpDir",    "helpDir", "helpDir",      OPTIONAL, PATH_VALUE,    "Help file directory"},
  {"ftsDir",    "d", "ftsDir",      OPTIONAL, PATH_VALUE,    "\tFTS executables directory"},
  {"sysDir",    "sys", "sysDir",      OPTIONAL, PATH_VALUE,    "\tFTS support file directory"},
  {"ftsName",    "n", "ftsName",      OPTIONAL, PATH_VALUE,    "Name of the FTS executable"},
  {"nBoards",    "nb", "nBoards",  OPTIONAL, INTEGER_VALUE,  "Number of Boards"},
  {"advances",  "adv", "<val, , >",  OPTIONAL, STRING_VALUE,  "Columns-separated list of scheduler advances (ISPW)"},
  {"throttle", "throttle", "<val, , >", OPTIONAL, STRING_VALUE,"Columns-separated list of 3 throttles (ISPW)"},
  {"logfile",    "logfile",  0,      OPTIONAL, BOOL_VALUE,    "Make a logfile"},
  {"minfile",    "minfile",  0,      OPTIONAL, BOOL_VALUE,    "Make a minfile for each loaded patch"},
  {"stderrCopy",  "stderr",  0,      OPTIONAL, BOOL_VALUE,    "Copy Max window to stderr"},
  {"linkOptions",  "lo", "linkOptions",  OPTIONAL, STRING_VALUE,  "Link options"},
  {"verbose",    "verbose", 0,      OPTIONAL, BOOL_VALUE,    "Verbose startup"},
  {"fontName",  "f", "fontName",  OPTIONAL, STRING_VALUE,  "Text font name"},
  {"fontSize",  "s", "fontSize",  OPTIONAL, INTEGER_VALUE,  "Text font size"},
  {"startupFile",  "c", "startupFile", OPTIONAL, PATH_VALUE,  "Startup configuration file, min format"},
  0};


static const char *
appname()
{
  static char buf[80];

  /* sprintf(buf, "Max_%d_%d",  FTS_MAJOR_RELEASE, FTS_MINOR_RELEASE); */
  sprintf(buf, "Max_Fts");
  return(buf);
}

static void
registerthem(void )
{
  struct _NXDefault def[sizeof(max_argdesc)/sizeof(argDesc) + 1];
  int i;
   
  for (i = 0; i < sizeof(max_argdesc)/sizeof(argDesc); i++)
    {
      
      def[i].name = (char *)max_argdesc[i].name;
      def[i].value = 0;
    }
  def[sizeof(max_argdesc)/sizeof(argDesc)].name = 0;
  def[sizeof(max_argdesc)/sizeof(argDesc)].value = 0;

  NXRegisterDefaults(appname(), def);
}




/* NOTE ON DEFAULTS AND SETTINGS VALUES LOCATION:
  - at startup, values (set on the command line, in the NXDefaluts database, in the User environment
    or in the .defs files, are retrieved by the getValues calls.
  - these values are then used in many different calls,
  - some values are kept in different modules after the calls. Max, the fts server, the manager and
    ftsd are modules that can't share the same memory space and have their owns values spaces.
  - the preferences and settings have their own mechanisms and values... how to maintain coherency ?

  A new policy to maintain the values coherency should rely on the general use of the getValue
  module. Some communication mechanism should avail a correct refresh of values.
 
  Some values could be known by an other module that Max (ftsd for example) that should receive
  (or request) them automaticaly.
 
  We could imagine getValue to work with lists of clients (function address + some tag) bound to
  variable names, and call the clients whenever the value changes.
 
  Max has to be cleaned, and each value (variable) should rely in a reference module (getValue..).
 
  Known modules that keep their own copy of values:
     - max/fts_max.c
   - max/t_text.c: see t_setfont, ...
   - mess/toolbox.c : see t_setdefpath,
   
  Known modules that set and get values:
     - max/charlie.m
   - max/prefer.m : uses toolbox.
   - in classes : many use toolbox.
 */

extern void utils_init(void);

main(argc, argv)
    char **argv;
{
  int        linktype;
  const char *fts_arch;
  const char *ftshost = 0;
  const char *rootDir, *sysDir;      
  int tmpi, i;
  const char *tmpc;

  /* Initialize the super default/env/site configuration system.
    Soon all of the code should be converted to use this call,
    (superset and compatible with the Next Defaults) */

  for (i = 0; i < argc; i++)
    {
      if (!argv[i])
   argv[i] = "-NextAteThisOne";
      else
   if (!strcmp("-verbose", argv[i]))
     enableErrorPrint(); /* getValue debug */
    }
   
  if (!initNameValue(appname(), max_argdesc, argc-1, argv+1))
    exit(1);
   
  if (getBoolOption("Version", 0))
    {
      printf("Max version %d.%d patch level %d %s\ncopyright 1992, 1995 IRCAM\n",    
       FTS_MAJOR_RELEASE, FTS_MINOR_RELEASE, FTS_PATCH_LEVEL, FTS_RELEASE_QUALIFICATION);   
      exit(0);
    }

  /* Note: if given in command line, already done:*/
  if (getBoolOption("verbose", 0))   enableErrorPrint(); /* getValue debug */
   
  [Maxapp new];
  setupmenus();
  registerthem(); /* @@@@@@@ WHY CALL THIS ??*/

  if (getBoolOption("stderrCopy", 0))   post_copytostd();

  utils_init(); /* Initialize symbol table */

  /**** kind of link ****/
  {
    const char *linktype_s;
      
    /* Get FTS_ARCH: */

    fts_arch = getStringOption("ftsArch", "ispw"); /* for the nextstep max, now ispw is the default */

    /*** Now determine the LINKTYPE: ****/
      
    linktype  = -1;
    linktype_s = getStringOption("linkType", 0);
      
    if (linktype_s)
      {
   if (! strcmp(linktype_s, "sink"))
     linktype = SINK_CONNECTION;
   else if (!    strcmp(linktype_s, "socket"))
     linktype = SOCKET_CONNECTION;
   else if (! strcmp(linktype_s, "pipe"))
     linktype = PIPE_CONNECTION;
   else if (! strcmp(linktype_s, "pipe"))
     linktype = PIPE_CONNECTION;
   else if (! strcmp(linktype_s, "named_pipe"))
     linktype = NAMED_PIPE_CONNECTION;
   else if (! strcmp(linktype_s, "named_pipe"))
     linktype = TTY_CONNECTION;
   else if (! strcmp(linktype_s, "ispw"))
     linktype = ISPW_CONNECTION; /* @@@@ ?? do some test ?? depend on the architecture ?? */
   else
     {
       printf("Error: connection type %s unknown on this arch.\n", linktype_s);
       cleanexit(1);
     }
      }

    if (ftshost = getStringOption("ftsHost", 0))
      {
   char *handle = (char *)malloc( strlen(ftshost) + 1);
      
   strcpy(handle, ftshost);
   ftshost = handle;
      }
      
    /*----------------------------*
      Default values:
      - if ftshost defined, but not linktype: then linktype = L_SOCKET.
      - if linktype and ftshost undefined: depends on arch:
      - L_DIRECT if some board possible,
      - L_PIPE on clients supporting pipes.
      - L_SOCKET else. (host = local)
      *-----------------------------*/

    if ((0 > (int)linktype) && (ftshost))
      {
   linktype = SOCKET_CONNECTION;   
   setStringOption("linkType", "socket");
      }
      
      
    if (0 > (int)linktype)
      {
   /* Default link, depens on arch: */
#if defined(FTS_CLIENT_USE_ISPW)
   linktype =  ISPW_CONNECTION;
   setStringOption("linkType", "ispw");

#elif (defined(FTS_CLIENT_USE_PIPES))
   linktype = PIPE_CONNECTION;
   setStringOption("linkType", "pipe");
#else
   linktype = SOCKET_CONNECTION;      
   setStringOption("linkType", "socket");
#endif
      }

    fprintf(stderr,"Max starting a %s connection to an %s fts\n",
       getStringOption("linkType", "?"), fts_arch);

  } /**** End kind of link ****/


  /**** Call this before any other h_ functions ****/

  if (! h_fts_init( linktype, getStringOption("linkName", 0), ftshost))
    {
      fprintf(stderr, "Unable to connect to FTS or to the daemon\n");
      exit(0);
    }

  /* Enable immediately the logfile if requested, so
    we get all the message logged */

  if (getBoolOption("logfile", 0))
    fts_open_log(".max_logfile", 0, 0);

  if (getBoolOption("minfile", 0))
    fts_enable_minfile();

  if (getBoolOption("verbose", 0))
    h_setverbose(1);


   
  {
    char mofo[100];
    sprintf(mofo, "Max version %d.%d patch level %d %s\ncopyright 1992, 1995 IRCAM\n",    
       FTS_MAJOR_RELEASE, FTS_MINOR_RELEASE, FTS_PATCH_LEVEL, FTS_RELEASE_QUALIFICATION);   
    pdisplay(mofo);
  }

   
  if (! strcmp(fts_arch, "ispw"))
    rootDir = getStringOption("rootDir", "/LocalApps/fts.1.3");
  else
    rootDir = getStringOption("rootDir", "/usr/local/fts.1.3");


  h_setftsdir(getStringOption("ftsDir", makeDefaultArchFilename(rootDir, "bin", fts_arch)));

  /* sysdir ignored if not ispw */
 
  if (! strcmp(fts_arch, "ispw"))
    {
      sysDir = getStringOption("sysDir", "/LocalApps/Ispw");
      h_setsysdir( sysDir);
    }

  h_setftsname( getStringOption("ftsName", "fts"));
   
  if (tmpc = getStringOption("fontName", 0))
    t_setfont(tmpc);

  if (tmpc = getStringOption("fontSize", 0))
    t_setfontsize(tmpc);


  /* Parameters that can be set in the machine file */

  /* the machine file is read *only* if the editor is running on a machine
    with the same architecture as the server; i.e. we hope we are running
    on the same machine as the server, otherwise the machine file
    make no sense
    */
     
  if (! strcmp(fts_arch, "ispw"))
    {
      /* We run ISPW on a next, so we first read the machine file,
    then, if the board option has been specified, we
    set it */

      h_read_min_file(makeDefaultFilename(sysDir, "local.cfg"));

      if (getIntOption("nBoards", -1) > 0)
   h_setnboards(getIntOption("nBoards", 0));

      h_waitnboards();   

      /* Notes that advances and throttle MUST be set after nboards;
    it is shadock shit, but not time to fix it*/

      h_setadvancesstring(getStringOption("advances", "5:5:5:5:5:5"));
      h_setthrottlestring(getStringOption("Throttle", "8:8:1"));      
    }
  else
    {
      /* other wise don't read the machine file, and set the
    system to monoprocessor */

      h_setmonoproc();
    }

  /******  blabla  *****/

  mess_init();
   
  {
    char dir[MAXPATHLEN+1];
    if (getwd(dir)) defvolume = gensym(dir);
  }

  h_start();
  max_startpolling();   

  /* Read the various config file  */

  h_read_min_file(getStringOption("startupFile", makeDefaultFilename(sysDir, "default.cfg")));

  if (! getBoolOption("-no26", 0))
    h_read_min_file(makeDefaultFilename(rootDir,
               "lib/config/twentysix.cfg"));

  if (tmpc = getStringOption("helpDir", 0))
    t_sethelppath(gensym(tmpc));
  else
    t_sethelppath(gensym(makeDefaultFilename(rootDir, "lib/help")));
   
  t_setdefpath(gensym(getStringOption("patchPath", "")));
  t_setpath(t_getdefpath());

  [NXApp run];
  fts_close_log();
  max_stoppolling();
  h_eof();
  [NXApp free];
}



nx_charlie  *charlie;   /* NXApp delegate */

setupmenus()
{
  id   myMenu,
    infomenu, filemenu, windmenu, editmenu, miscmenu, classmenu;

  charlie = [nx_charlie new];
  [NXApp setDelegate: charlie];

  /* menus */
  myMenu = [Menu newTitle:appname()];

  infomenu = [Menu newTitle:"Info"];
  [[infomenu addItem:"Info Panel..."
       action:@selector(info:)
       keyEquivalent:0]
    setTarget:charlie];
  [[infomenu addItem:"Preferences..."
       action:@selector(preferences:)
       keyEquivalent:'1']
    setTarget:charlie];
  [[infomenu addItem:"Help..."
       action:@selector(help:)
       keyEquivalent:'?']
    setTarget:charlie];
  [infomenu sizeToFit];
  [myMenu setSubmenu: infomenu
     forItem: [myMenu addItem:"Info"
           action:0
           keyEquivalent:0]];

  filemenu = [Menu newTitle:"File"];
  [[filemenu addItem:"Open..."
       action:@selector(open:)
       keyEquivalent:'o']
    setTarget:charlie];
  [[filemenu addItem:"New Patcher"
       action:@selector(new_patch:)
       keyEquivalent:'n']
    setTarget:charlie];
  [filemenu addItem:"Save"
       action:@selector(save:)
       keyEquivalent:'s'];
  [filemenu addItem:"Save as..."
       action:@selector(saveAs:)
       keyEquivalent:'S'];
  [filemenu sizeToFit];
  [myMenu setSubmenu: filemenu
     forItem: [myMenu addItem:"File"
           action:0
           keyEquivalent:0]];

  editmenu = [Menu newTitle:"Edit"];
  [editmenu addItem:"Cut"
       action:@selector(cut:)
       keyEquivalent:'x'];
  [editmenu addItem:"Copy"
       action:@selector(copy:)
       keyEquivalent:'c'];
  [editmenu addItem:"Paste"
       action:@selector(paste:)
       keyEquivalent:'v'];
  [editmenu addItem:"Duplicate"
       action:@selector(dup:)
       keyEquivalent:'d'];
  [editmenu addItem:"Select All"
       action:@selector(selectall:)
       keyEquivalent:'a'];
  [editmenu sizeToFit];
  [myMenu setSubmenu: editmenu
     forItem: [myMenu addItem:"Edit"
           action:0
           keyEquivalent:0]];


  windmenu = [Menu newTitle:"Windows"];
  [windmenu addItem:"Close"
       action:@selector(close:)
       keyEquivalent:'w'];
  [windmenu addItem:"Miniaturize"
       action:@selector(miniaturize:)
       keyEquivalent:'M'];
  [NXApp setWindowsMenu: windmenu];
  [myMenu setSubmenu: windmenu
     forItem: [myMenu addItem:"Windows"
           action:0
           keyEquivalent:0]];


  [myMenu addItem:"Print ..."
     action:@selector(print:)
     keyEquivalent:'p'];

  miscmenu = [Menu newTitle:"Font"];
  [miscmenu addItem:"9" action:@selector(s9:) keyEquivalent:0];
  [miscmenu addItem:"10" action:@selector(s10:) keyEquivalent:0];
  [miscmenu addItem:"12" action:@selector(s12:) keyEquivalent:0];
  [miscmenu addItem:"14" action:@selector(s14:) keyEquivalent:0];
  [miscmenu addItem:"16" action:@selector(s16:) keyEquivalent:0];
  [miscmenu addItem:"18" action:@selector(s18:) keyEquivalent:0];
  [miscmenu addItem:"20" action:@selector(s20:) keyEquivalent:0];
  [miscmenu addItem:"24" action:@selector(s24:) keyEquivalent:0];
  [miscmenu addItem:"FONT BOMB" action:@selector(fontbomb:) keyEquivalent:0];
  [miscmenu sizeToFit];
  [myMenu setSubmenu: miscmenu forItem: [myMenu addItem:"Font"
                  action:0 keyEquivalent:0]];

  miscmenu = [Menu newTitle:"Misc"];
  [miscmenu addItem:"Edit" action:@selector(edit:) keyEquivalent:'e'];
  [miscmenu addItem:"Stretch" action:@selector(stretch:) keyEquivalent:0];
  [miscmenu addItem:"Contract" action:@selector(contract:) keyEquivalent:0];
  [[miscmenu addItem:"Settings..."
       action:@selector(settings:) keyEquivalent:'2'] setTarget:charlie];
  [[miscmenu addItem:"Restart..."
       action:@selector(restart:) keyEquivalent:'3'] setTarget:charlie];
  [miscmenu addItem:"Inspect..."
       action:@selector(inspect:) keyEquivalent:'4'];
  [miscmenu addItem:"Find..."
       action:@selector(find:) keyEquivalent:'f'];
  [miscmenu addItem:"Find again"
       action:@selector(findagain:) keyEquivalent:'g'];
  [miscmenu sizeToFit];
  [myMenu setSubmenu: miscmenu forItem: [myMenu addItem:"Misc"
                  action:0 keyEquivalent:0]];

  [myMenu addItem:"Hide"
     action:@selector(hide:)
     keyEquivalent:'h'];
  [myMenu addItem:"Quit"
     action:@selector(terminate:)
     keyEquivalent:'q'];
  [myMenu sizeToFit];
  [NXApp setMainMenu:myMenu];
  [myMenu display];

}

cleanexit(n)
{
  h_eof();
  [NXApp free];
  exit(n);
}

id focusview()
{
  return([NXApp focusView]);
}

void max_restart()
{
  int count = [[NXApp windowList] count], i;

  /* We don't restart the sounddisks here, because in any
    case we have to reload the patch; this will restart all
    the soundservers; on the contrary, we need to close all
    the open dtd servers
    */

  dtd_close_all_servers();


  toolbox__restart();
  h_start();
  for (i=0; i<count; i++)
    {
      id window = [[NXApp windowList] objectAt:i];
      if ([window respondsTo:@selector(restart_fts)])
   [window restart_fts];
    }
}

void max_find(s, again)
    char *s;
{
  int nwind, *windows, i;
  [NXApp getWindowNumbers: &windows count:&nwind];
  for (i=0; i < nwind; i++)
    {
      id window = [NXApp findWindow: windows[i]];
      if (window && [window respondsTo:@selector(find::)])
   {
     [window find: s :again];
     return;
   }
    }
}

id max_front()
{
  int nwind, *windows, i;
  [NXApp getWindowNumbers: &windows count:&nwind];
  for (i=0; i < nwind; i++)
    {
      id window = [NXApp findWindow: windows[i]];
      if (window && [window respondsTo:@selector(wind)])
   return (window);
    }
  return (0);
}

int max_canrestart()
{
  int count = [[NXApp windowList] count], i;
  for (i=0; i<count; i++)
    {
      id window = [[NXApp windowList] objectAt:i];
      if ([window respondsTo:@selector(setinspector:)]) return (0);
    }
  return (1);
}
Crazy

Dernière édition par jbfairlight le Mer 25 Mai - 19:37, édité 1 fois

descriptionRe: Qu'est ce ...

more_horiz
Max par ci, Max par là... il est libre Max, Mad Max, Max la menace ?! No idea Cry

_________________
[Vous devez être inscrit et connecté pour voir ce lien]
[Vous devez être inscrit et connecté pour voir ce lien]
[Vous devez être inscrit et connecté pour voir ce lien]
[Vous devez être inscrit et connecté pour voir ce lien]

descriptionRe: Qu'est ce ...

more_horiz
Une partie des sources en Objective-C de MAX / FTS.

descriptionRe: Qu'est ce ...

more_horiz
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum