This is xvmisc.c in view mode; [Download] [Up]
/* * xvmisc.c - random 'handy' routines used in XV * * Author: John Bradley, University of Pennsylvania * (bradley@cis.upenn.edu) * * Contains: * Window CreateWindow(name, clname, geom, w, h, fg, bg, usesize) * void CenterString(win, str, x, y) * void ULineString(win, str, x, y) * int StringWidth(str) * void FakeButtonPress(bptr) * void FakeKeyPress(win, keysym); * void GenExpose(win, x, y, w, h); * void xvDestroyImage(XImage *); * void DimRect(win, x, y, w, h, bg); * void Draw3dRect(win, x,y,w,h, inout, bwidth, hicol, locol); * void SetCropString(active) * void Warning() * void FatalError(str) * void Quit(int) * void LoadFishCursors() * void WaitCursor() * void SetCursors(int) * char *BaseName(char *) * void DrawTempGauge(win, x,y,w,h, percent, fg,bg,hi,lo) * void xvbcopy(src, dst, length) * int xvbcmp (s1, s2, length) * void xvbzero(s, length) * void Timer(milliseconds) */ /* Copyright Notice * ================ * Copyright 1989, 1990, 1991, 1992, 1993 by John Bradley * * Permission to use, copy, and distribute XV in its entirety, for * non-commercial purposes, is hereby granted without fee, provided that * this license information and copyright notice appear in all copies. * * Note that distributing XV 'bundled' in with ANY product is considered * to be a 'commercial purpose'. * * Also note that any copies of XV that are distributed MUST be built * and/or configured to be in their 'unregistered copy' mode, so that it * is made obvious to the user that XV is shareware, and that they should * consider donating, or at least reading this License Info. * * The software may be modified for your own purposes, but modified * versions may NOT be distributed without prior consent of the author. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the author be held liable for any damages * arising from the use of this software. * * If you would like to do something with XV that this copyright * prohibits (such as distributing it with a commercial product, * using portions of the source in some other program, etc.), please * contact the author (preferably via email). Arrangements can * probably be worked out. * * XV is shareware for PERSONAL USE only. You may use XV for your own * amusement, and if you find it nifty, useful, generally cool, or of * some value to you, your non-deductable donation would be greatly * appreciated. $25 is the suggested donation, though, of course, * larger donations are quite welcome. Folks who donate $25 or more * can receive a Real Nice bound copy of the XV manual for no extra * charge. * * Commercial, government, and institutional users MUST register their * copies of XV, for the exceedingly REASONABLE price of just $25 per * workstation/X terminal. Site licenses are available for those who * wish to run XV on a large number of machines. Contact the author * for more details. * * The author may be contacted via: * US Mail: John Bradley * 1053 Floyd Terrace * Bryn Mawr, PA 19010 * * Phone: (215) 898-8813 * EMail: bradley@cis.upenn.edu */ #define NEEDSTIME #include "xv.h" #include "bitmaps.h" #ifdef __STDC__ static void set_cursors(Cursor, Cursor); #else static void set_cursors(); #endif static Atom atom_DELWIN = 0; static Atom atom_PROTOCOLS = 0; /***************************************************/ void StoreDeleteWindowProp (win) Window win; { if (! atom_DELWIN) atom_DELWIN = XInternAtom (theDisp, "WM_DELETE_WINDOW", FALSE); /* the following fakes 'XSetWMProtocols(theDisp, win, &atom_DELWIN, 1);' */ if (! atom_PROTOCOLS) atom_PROTOCOLS = XInternAtom (theDisp, "WM_PROTOCOLS", False); if (atom_PROTOCOLS == None) return; XChangeProperty(theDisp, win, atom_PROTOCOLS, XA_ATOM, 32, PropModeReplace, (unsigned char *) &atom_DELWIN, 1); } /***************************************************/ Window CreateWindow(name,clname,geom,defw,defh,fg,bg,usesize) char *name, *clname, *geom; int defw,defh,usesize; unsigned long fg, bg; { Window win; XSetWindowAttributes xswa; unsigned long xswamask; XWMHints xwmh; XSizeHints hints; int i,x,y,w,h; XClassHint classh; /* note: if usesize, w,h are extracted from geom spec, otherwise defw,defh are used */ x = y = 1; i = XParseGeometry(geom,&x,&y, (unsigned int *) &w, (unsigned int *) &h); if ((i&XValue || i&YValue)) hints.flags = USPosition; else hints.flags = PPosition; if (!usesize || !(i&WidthValue)) w = defw; if (!usesize || !(i&HeightValue)) h = defh; hints.flags |= USSize; if (i&XValue && i&XNegative) x = dispWIDE - w - abs(x); if (i&YValue && i&YNegative) y = dispHIGH - h - abs(y); #define VROOT_TRANS #ifdef VROOT_TRANS if (vrootW != rootW) { /* virtual window manager running */ int x1,y1; Window child; XTranslateCoordinates(theDisp, rootW, vrootW, x, y, &x1, &y1, &child); if (DEBUG) fprintf(stderr,"translate: %d,%d -> %d,%d\n",x,y,x1,y1); x = x1; y = y1; } #endif hints.x = x; hints.y = y; hints.width = w; hints.height = h; if (!usesize) { hints.min_width = w; hints.min_height = h; hints.max_width = w; hints.max_height = h; hints.flags |= PMaxSize | PMinSize; } xswa.background_pixel = bg; xswa.border_pixel = fg; xswa.colormap = theCmap; xswa.bit_gravity = StaticGravity; xswamask = CWBackPixel | CWBorderPixel | CWColormap; if (!usesize) xswamask |= CWBitGravity; win = XCreateWindow(theDisp, rootW, x, y, w, h, bwidth, dispDEEP, InputOutput, theVisual, xswamask, &xswa); if (!win) return(win); /* leave immediately if couldn't create */ XSetStandardProperties(theDisp, win, name, name, None, NULL, 0, &hints); xwmh.input = True; xwmh.flags = InputHint; if (iconPix) { xwmh.icon_pixmap = iconPix; xwmh.flags |= IconPixmapHint; } XSetWMHints(theDisp, win, &xwmh); if (clname && strlen(clname)) { classh.res_name = "xv"; classh.res_class = clname; XSetClassHint(theDisp, win, &classh); StoreDeleteWindowProp(win); } return(win); } /**************************************************/ void CenterString(win,str,x,y) Window win; char *str; int x,y; { XDrawString(theDisp, win, theGC, CENTERX(mfinfo, x, str), CENTERY(mfinfo, y), str, strlen(str)); } /**************************************************/ void ULineString(win,str,x,y) Window win; char *str; int x,y; { XDrawString(theDisp, win, theGC, x, y, str, strlen(str)); XDrawLine(theDisp, win, theGC, x, y+DESCENT-1, x+StringWidth(str), y+DESCENT-1); } /**************************************************/ int StringWidth(str) char *str; { return(XTextWidth(mfinfo, str, strlen(str))); } /***********************************/ void FakeButtonPress(bp) BUTT *bp; { /* called when a button keyboard equivalent has been pressed. 'fakes' a ButtonPress event in the button, which A) makes the button blink, and B) falls through to ButtonPress command dispatch code */ Window rW, cW; int x, y, rx, ry; unsigned int mask; XButtonEvent ev; ev.type = ButtonPress; ev.send_event = True; ev.display = theDisp; ev.window = bp->win; ev.root = rootW; ev.subwindow = (Window) NULL; ev.x = bp->x; ev.y = bp->y; ev.state = 0; ev.button = Button1; XSendEvent(theDisp, bp->win, False, NoEventMask, (XEvent *) &ev); /* if button1 is pressed, loop until RELEASED to avoid probs in BTTrack */ while (XQueryPointer(theDisp, rootW, &rW,&cW,&rx,&ry,&x,&y,&mask)) { if (!(mask & Button1Mask)) break; /* button released */ } } /************************************************************************/ void FakeKeyPress(win, ksym) Window win; KeySym ksym; { XKeyEvent ev; ev.type = KeyPress; ev.send_event = True; ev.display = theDisp; ev.window = win; ev.root = rootW; ev.subwindow = (Window) NULL; ev.time = CurrentTime; ev.x = ev.y = ev.x_root = ev.y_root = 0; ev.state = 0; ev.keycode = XKeysymToKeycode(theDisp, ksym); ev.same_screen = True; XSendEvent(theDisp, win, False, NoEventMask, (XEvent *) &ev); XFlush(theDisp); } /***********************************/ void GenExpose(win, x, y, w, h) Window win; int x,y,w,h; { /* generates an expose event on 'win' of the specified rectangle. Unlike XClearArea(), it doesn't clear the rectangular region */ XExposeEvent ev; ev.type = Expose; ev.send_event = True; ev.display = theDisp; ev.window = win; ev.x = x; ev.y = y; ev.width = w; ev.height = h; ev.count = 0; XSendEvent(theDisp, win, False, NoEventMask, (XEvent *) &ev); } /***********************************/ void xvDestroyImage(image) XImage *image; { /* called in place of XDestroyImage(). Explicitly destroys *BOTH* the data and the structure. XDestroyImage() doesn't seem to do this on all systems. Also, can be called with a NULL image pointer */ if (image) { /* free data by hand, since XDestroyImage is vague about it */ if (image->data) free(image->data); image->data = NULL; XDestroyImage(image); } } /***********************************/ void DimRect(win, x, y, w, h, bg) Window win; int x,y,w,h; u_long bg; { /* stipple a rectangular region by drawing 'bg' where there's 1's in the stipple pattern */ XSetFillStyle(theDisp, theGC, FillStippled); XSetStipple(theDisp, theGC, dimStip); XSetForeground(theDisp, theGC, bg); XFillRectangle(theDisp,win,theGC,x,y,w,h); XSetFillStyle(theDisp, theGC, FillSolid); } /**************************************************/ void Draw3dRect(win, x,y,w,h, inout, bwidth, hi, lo, bg) Window win; int x,y,w,h,inout,bwidth; unsigned long hi, lo, bg; { int i,r; if (ctrlColor) { /* draw top-left */ if (inout == R3D_OUT) XSetForeground(theDisp, theGC, hi); else XSetForeground(theDisp, theGC, lo); for (i=0; i<bwidth; i++) { XDrawLine(theDisp, win, theGC, x+i, y+h-i, x+i, y+i); XDrawLine(theDisp, win, theGC, x+i, y+i, x+w-i, y+i); } /* draw bot-right */ if (inout == R3D_OUT) XSetForeground(theDisp, theGC, lo); else XSetForeground(theDisp, theGC, hi); for (i=0; i<bwidth; i++) { XDrawLine(theDisp, win, theGC, x+i+1, y+h-i, x+w-i, y+h-i); XDrawLine(theDisp, win, theGC, x+w-i, y+h-i, x+w-i, y+i+1); } /* draw diagonals */ XSetForeground(theDisp, theGC, bg); for (i=0; i<bwidth; i++) { XDrawPoint(theDisp, win, theGC, x+i, y+h-i); XDrawPoint(theDisp, win, theGC, x+w-i, y+i); } } } /**************************************************/ void SetCropString(active) int active; { /* sets the crop string in the info box to be correct. should be called whenever 'but[BCROP].active', cXOFF,cYOFF,cWIDE,cHIGH are changed */ /* if 'active' there's a cropping rectangle drawn on the picture. the string should reflect that. */ if (active) { /* figure out current cropping rectangle in 'pic' coordinates */ int x,y,x1,y1,w,h; int cx,cy,dx,dy; if (crx1<crx2) cx=crx1; else cx=crx2; if (cry1<cry2) cy=cry1; else cy=cry2; dx = abs(crx1-crx2); dy = abs(cry1-cry2); x = cXOFF + (cx * cWIDE) / eWIDE; y = cYOFF + (cy * cHIGH) / eHIGH; x1 = cXOFF + ((cx+dx) * cWIDE) / eWIDE; y1 = cYOFF + ((cy+dy) * cHIGH) / eHIGH; w = (x1 - x) + 1; h = (y1 - y) + 1; if (w<1) w = 1; if (x+w > pWIDE) w = pWIDE - x; if (h<1) h = 1; if (y+h > pHIGH) h = pHIGH - y; SetISTR(ISTR_CROP, "%dx%d rectangle starting at %d,%d", w, h, x, y); } else { /* cropping rectangle is turned off */ if (cpic != pic) SetISTR(ISTR_CROP, "%dx%d rectangle starting at %d,%d", cWIDE, cHIGH, cXOFF, cYOFF); else SetISTR(ISTR_CROP, "<none>"); } } /***********************************/ void Warning() { char *st; /* give 'em time to read message */ if (infoUp || ctrlUp || anyBrowUp) sleep(3); else { st = GetISTR(ISTR_INFO); OpenAlert(st); sleep(3); CloseAlert(); } } /***********************************/ void FatalError (identifier) char *identifier; { fprintf(stderr, "%s: %s\n",cmd, identifier); Quit(-1); } /***********************************/ void Quit(i) int i; { /* called when the program exits. frees everything explictly created EXCEPT allocated colors. This is used when 'useroot' is in operation, as we have to keep the alloc'd colors around, but we don't want anything else to stay */ if (!theDisp) exit(i); /* called before connection opened */ if (useroot && i==0) { /* save the root info */ SaveRootInfo(); /* kill the various windows, since we're in RetainPermanent mode now */ if (dirW) XDestroyWindow(theDisp, dirW); if (infoW) XDestroyWindow(theDisp, infoW); if (ctrlW) XDestroyWindow(theDisp, ctrlW); if (gamW) XDestroyWindow(theDisp, gamW); KillBrowseWindows(); if (psW) XDestroyWindow(theDisp, psW); #ifdef HAVE_JPEG if (jpegW) XDestroyWindow(theDisp, jpegW); #endif #ifdef HAVE_TIFF if (tiffW) XDestroyWindow(theDisp, tiffW); #endif /* if NOT using stdcmap for images, free stdcmap */ if (colorMapMode != CM_STDCMAP) { int j; for (j=0; j<stdnfcols; j++) xvFreeColors(theDisp, theCmap, &stdfreecols[j], 1, 0L); } /* free browCmap, if any */ if (browPerfect && browCmap) XFreeColormap(theDisp, browCmap); XFlush(theDisp); } KillPageFiles(pageBaseName, numPages); exit(i); } static Cursor flcurs, fl1curs, fmcurs, fr1curs, frcurs; /***********************************/ void LoadFishCursors() { #define fc_w 16 #define fc_h 16 Pixmap flpix,flmpix,fmpix,fmmpix,frpix,frmpix; Pixmap fl1pix, fl1mpix, fr1pix, fr1mpix; XColor fg, bg; flcurs = fl1curs = fmcurs = fr1curs = frcurs = (Pixmap) NULL; flpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_left_bits, fc_w, fc_h, 1, 0, 1); flmpix= XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_leftm_bits, fc_w, fc_h, 1, 0, 1); fl1pix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_left1_bits, fc_w, fc_h, 1, 0, 1); fl1mpix= XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_left1m_bits, fc_w, fc_h, 1, 0, 1); fmpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_mid_bits, fc_w, fc_h, 1, 0, 1); fmmpix= XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_midm_bits, fc_w, fc_h, 1, 0, 1); fr1pix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_right1_bits, fc_w, fc_h, 1, 0, 1); fr1mpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_right1m_bits, fc_w, fc_h, 1, 0, 1); frpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_right_bits, fc_w, fc_h, 1, 0, 1); frmpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_rightm_bits, fc_w, fc_h, 1, 0, 1); if (!flpix || !flmpix || !fmpix || !fmmpix || !frpix || !frmpix || !fl1pix || !fl1mpix || !fr1pix || !fr1mpix) return; fg.red = fg.green = fg.blue = 0; bg.red = bg.green = bg.blue = 0xffff; flcurs = XCreatePixmapCursor(theDisp, flpix, flmpix, &fg, &bg, 8,8); fl1curs= XCreatePixmapCursor(theDisp, fl1pix,fl1mpix,&fg, &bg, 8,8); fmcurs = XCreatePixmapCursor(theDisp, fmpix, fmmpix, &fg, &bg, 8,8); fr1curs= XCreatePixmapCursor(theDisp, fr1pix,fr1mpix,&fg, &bg, 8,8); frcurs = XCreatePixmapCursor(theDisp, frpix, frmpix, &fg, &bg, 8,8); if (!flcurs || !fmcurs || !frcurs || !fl1curs || !fr1curs) { flcurs = fmcurs = frcurs = (Cursor) NULL; } } static int fishno=0; /***********************************/ void WaitCursor() { SetCursors(fishno); fishno = (fishno+1) % 8; } /***********************************/ void SetCursors(n) int n; { Cursor c; c = cross; /* if n < 0 sets normal cursor in all windows n = 0..6 cycles through fish cursors */ if (n<0) { if (showzoomcursor) set_cursors(zoom, arrow); else set_cursors(cross, arrow); fishno = 0; } else if (flcurs) { /* was able to load the cursors */ switch (n%8) { case 0: c = flcurs; break; case 1: c = fl1curs; break; case 2: c = fmcurs; break; case 3: c = fr1curs; break; case 4: c = frcurs; break; case 5: c = fr1curs; break; case 6: c = fmcurs; break; case 7: c = fl1curs; break; } set_cursors(c,c); } XFlush(theDisp); } static void set_cursors(mainc, otherc) Cursor mainc, otherc; { if (!useroot && mainW) XDefineCursor(theDisp, mainW, mainc); if (infoW) XDefineCursor(theDisp, infoW, otherc); if (ctrlW) XDefineCursor(theDisp, ctrlW, otherc); if (dirW) XDefineCursor(theDisp, dirW, otherc); if (gamW) XDefineCursor(theDisp, gamW, otherc); if (psW) XDefineCursor(theDisp, psW, otherc); SetBrowseCursor(otherc); #ifdef HAVE_JPEG if (jpegW) XDefineCursor(theDisp, jpegW, otherc); #endif #ifdef HAVE_TIFF if (tiffW) XDefineCursor(theDisp, tiffW, otherc); #endif } /***************************************************/ char *BaseName(fname) char *fname; { char *basname; /* given a complete path name ('/foo/bar/weenie.gif'), returns just the 'simple' name ('weenie.gif'). Note that it does not make a copy of the name, so don't be modifying it... */ basname = rindex(fname, '/'); if (!basname) basname = fname; else basname++; return basname; } /***************************************************/ void DrawTempGauge(win, x,y,w,h, ratio, fg,bg,hi,lo) Window win; int x,y,w,h; double ratio; u_long fg,bg,hi,lo; { /* draws a 'temprature'-style horizontal progess meter in the specified window, at the specified location */ int barwide, maxwide; XSetForeground(theDisp, theGC, fg); XDrawRectangle(theDisp, win, theGC, x,y,w,h); if (ctrlColor) { Draw3dRect(win, x+1,y+1,w-2,h-2, R3D_IN, 2, hi, lo, bg); maxwide = w-5; barwide = (int) (maxwide * ratio + 0.5); XSetForeground(theDisp, theGC, fg); XFillRectangle(theDisp, win, theGC, x+3, y+3, barwide, h-5); XDrawLine(theDisp, win, theGC, x+3+barwide, y+h/2 + 0, x+w-3, y+h/2 + 0); XSetForeground(theDisp, theGC, lo); XDrawLine(theDisp, win, theGC, x+3+barwide, y+h/2 + 1, x+w-3, y+h/2 + 1); XSetForeground(theDisp, theGC, hi); XDrawLine(theDisp, win, theGC, x+3+barwide, y+h/2 + 2, x+w-3, y+h/2 + 2); XSetForeground(theDisp, theGC, bg); XFillRectangle(theDisp, win, theGC, x+3+barwide, y+3, (maxwide-barwide), (h/2 - 3)); XFillRectangle(theDisp, win, theGC, x+3+barwide, y+h/2 + 3, (maxwide-barwide), ((h-3) - (h/2+3)) + 1); } else { maxwide = w-1; barwide = (int) (maxwide * ratio + 0.5); XSetForeground(theDisp, theGC, fg); XFillRectangle(theDisp, win, theGC, x+1, y+1, barwide, h-1); XDrawLine(theDisp, win, theGC, x+1+barwide, y+h/2, x+w-1, y+h/2); XSetForeground(theDisp, theGC, bg); XFillRectangle(theDisp, win, theGC, x+1+barwide, y+1, maxwide-barwide, h/2 - 1); XFillRectangle(theDisp, win, theGC, x+1+barwide, y+h/2 + 1, maxwide-barwide, ((h-1) - (h/2+1)) + 1); } XFlush(theDisp); } /***************************************************/ void XVDeletedFile(fullname) char *fullname; { /* called whenever a file has been deleted. Updates browser & dir windows, if necessary */ BRDeletedFile(fullname); DIRDeletedFile(fullname); } /***************************************************/ void XVCreatedFile(fullname) char *fullname; { /* called whenever a file has been deleted. Updates browser & dir windows, if necessary */ BRCreatedFile(fullname); DIRCreatedFile(fullname); } /***************************************************/ void xvbcopy(src, dst, len) char *src, *dst; int len; { /* Modern OS's (Solaris, etc.) frown upon the use of bcopy(), * and only want you to use memcpy(). However, memcpy() is broken, * in the sense that it doesn't properly handle overlapped regions * of memory. This routine does, and also has its arguments in * the same order as bcopy() did, without using bcopy(). */ /* determine if the regions overlap * * 3 cases: src=dst, src<dst, src>dst * * if src=dst, they overlap completely, but nothing needs to be moved * if src<dst and src+len>dst then they overlap * if src>dst and src<dst+len then they overlap */ if (src==dst || len<=0) return; /* nothin' to do */ if (src<dst && src+len>dst) { /* do a backward copy */ src = src + len - 1; dst = dst + len - 1; for ( ; len>0; len--, src--, dst--) *dst = *src; } else { /* they either overlap (src>dst) or they don't overlap */ /* do a forward copy */ for ( ; len>0; len--, src++, dst++) *dst = *src; } } /***************************************************/ int xvbcmp (s1, s2, len) char *s1, *s2; int len; { for ( ; len>0; len--, s1++, s2++) { if (*s1 < *s2) return -1; else if (*s1 > *s2) return 1; } return 0; } /***************************************************/ void xvbzero(s, len) char *s; int len; { for ( ; len>0; len--) *s++ = 0; } /***************************************************/ static int timerdone; /*******/ static void onalarm() /*******/ { timerdone=1; } /*******/ void Timer(msec) /* waits for 'n' milliseconds */ int msec; /*******/ { long usec; if (msec <= 0) return; usec = (long) msec * 1000; #ifdef VMS { float ftime; ftime = msec / 1000.0; lib$wait(&ftime); return; } #endif #ifdef sgi { float ticks_per_msec; long ticks; ticks_per_msec = (float) CLK_TCK / 1000.0; ticks = (long) ((float) msec * ticks_per_msec); sginap(ticks); return; } #endif #ifdef sco { int dummy; poll(&dummy, 0, msec); return; } #endif #ifdef USLEEP usleep(usec); return; #endif #ifdef NOTIMER return; #endif #ifndef VMS /* VMS hates multi-line #ifdef's */ # if !defined(sgi) && !defined(sco) && !defined(USLEEP) && !defined(NOTIMER) { /* we aren't doing it any of the neat/clean/simple ways. Do it the old clunky way, using setitimer()... */ struct itimerval it; xvbzero((char *) &it, sizeof(it)); if (usec>=1000000L) { /* more than 1 second */ it.it_value.tv_sec = usec / 1000000L; usec %= 1000000L; } it.it_value.tv_usec = usec; timerdone=0; signal(SIGALRM,onalarm); setitimer(ITIMER_REAL, &it, (struct itimerval *)0); while (1) { HOLD_SIG; /* note: have to block, so that ALRM */ if (timerdone) break; /* doesn't occur between 'if (timerdone)' */ else PAUSE_SIG; /* and calling PAUSE_SIG */ } RELEASE_SIG; /* turn ALRM blocking off */ signal(SIGALRM,SIG_DFL); } # endif /* !(sgi || sco || USLEEP || NOTIMER) */ #endif /* !VMS */ }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.