screen_savers: Import Nebula.
This is an unmodified version of Axel's "Nebula" screensaver, except with the RSRC converted to an RDEF.
This commit is contained in:
@@ -0,0 +1,793 @@
|
||||
/* Nebula - a screen saver for the BeOS
|
||||
**
|
||||
** Copyright (c) 2001-2004 pinc Software. All Rights Reserved.
|
||||
** Effect from corTeX / Optimum
|
||||
*/
|
||||
|
||||
|
||||
#include <AppKit.h>
|
||||
#include <InterfaceKit.h>
|
||||
#include <Window.h>
|
||||
#include <ScreenSaver.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define VERSION_STRING "1.2.0 (August 21st 2004)"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y,z,r;
|
||||
} p3;
|
||||
|
||||
typedef float matrix[3][3];
|
||||
|
||||
#define GMAX 5000
|
||||
p3 gal[GMAX];
|
||||
float precos[512];
|
||||
float presin[512];
|
||||
|
||||
typedef unsigned short word;
|
||||
|
||||
extern "C" {
|
||||
void memshset(char *dst, int center_shade,int fixed_shade, int length_2);
|
||||
void mblur(char *src, int nbpixels);
|
||||
|
||||
void draw_stars320(char *, char);
|
||||
void draw_stars512(char *, char);
|
||||
void draw_stars576(char *, char);
|
||||
void draw_stars640(char *, char);
|
||||
void draw_stars800(char *, char);
|
||||
void draw_stars1024(char *, char);
|
||||
void draw_stars1152(char *, char);
|
||||
void draw_stars1280(char *, char);
|
||||
void draw_stars1400(char *, char);
|
||||
void draw_stars1600(char *, char);
|
||||
}
|
||||
|
||||
const uint32 kMsgWidth = 'widt';
|
||||
const uint32 kMsgColorScheme = 'cols';
|
||||
const uint32 kMsgBlankBorders = 'blbr';
|
||||
const uint32 kMsgMotionBlur = 'blur';
|
||||
const uint32 kMsgSpeed = 'sped';
|
||||
const uint32 kMsgFrames = 'mfps';
|
||||
|
||||
void (*gDrawStarFunc)(char *,char);
|
||||
float gSpeed;
|
||||
bool gMotionBlur;
|
||||
int32 gSettingsWidth;
|
||||
int32 gWidth;
|
||||
int32 gHeight;
|
||||
float gMaxFramesPerSecond;
|
||||
BBitmap *gBitmap;
|
||||
BScreenSaver *gScreenSaver;
|
||||
uint32 gPalette[256];
|
||||
int8 gPaletteScheme,gBlankBorders;
|
||||
char *gBuffer8; /* working 8bit buffer */
|
||||
|
||||
|
||||
inline float
|
||||
ocos(float a)
|
||||
{
|
||||
return (precos[(int)(a*256/M_PI) & 511]);
|
||||
}
|
||||
|
||||
inline float
|
||||
osin(float a)
|
||||
{
|
||||
return (presin[(int)(a*256/M_PI) & 511]);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mulmat(matrix *a, matrix *b, matrix *c)
|
||||
{
|
||||
int i,j;
|
||||
|
||||
for (i = 0;i < 3;i++) {
|
||||
for (j = 0;j < 3;j++) {
|
||||
(*c)[i][j] = (*a)[i][0] * (*b)[0][j] +
|
||||
(*a)[i][1] * (*b)[1][j] +
|
||||
(*a)[i][2] * (*b)[2][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
mulvec(matrix *a, float *x, float *y, float *z)
|
||||
{
|
||||
float nx = *x,ny = *y,nz = *z;
|
||||
|
||||
*x = nx*(*a)[0][0] + ny*(*a)[0][1] + nz*(*a)[0][2];
|
||||
*y = nx*(*a)[1][0] + ny*(*a)[1][1] + nz*(*a)[1][2];
|
||||
*z = nx*(*a)[2][0] + ny*(*a)[2][1] + nz*(*a)[2][2];
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
setrmat(float a, float b, float c, matrix *m)
|
||||
{
|
||||
int i,j;
|
||||
for (i = 0;i < 3;i++)
|
||||
for (j = 0;j < 3;j++)
|
||||
(*m)[i][j] = (float)(i == j);
|
||||
|
||||
if (a != 0) {
|
||||
(*m)[0][0] = cos(a); (*m)[0][1] = sin(a);
|
||||
(*m)[1][0] = sin(a); (*m)[1][1] = -cos(a);
|
||||
return;
|
||||
}
|
||||
if (b != 0) {
|
||||
(*m)[0][0] = cos(b); (*m)[0][2] = sin(b);
|
||||
(*m)[2][0] = sin(b); (*m)[2][2] = -cos(b);
|
||||
return;
|
||||
}
|
||||
(*m)[1][1] = cos(c); (*m)[1][2] = sin(c);
|
||||
(*m)[2][1] = sin(c); (*m)[2][2] = -cos(c);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
rotate3d(float *xr, float *yr, float *zr, /* point to rotate */
|
||||
float ax, float ay, float az) /* the 3 angles (order ?..) */
|
||||
{
|
||||
float xr2, yr2, zr2;
|
||||
|
||||
xr2 = (*xr*ocos(az) + *yr*osin(az));
|
||||
yr2 = (*xr*osin(az) - *yr*ocos(az));
|
||||
*xr = xr2;
|
||||
*yr = yr2;
|
||||
|
||||
xr2 = (*xr*ocos(ay) + *zr*osin(ay));
|
||||
zr2 = (*xr*osin(ay) - *zr*ocos(ay));
|
||||
*xr = xr2;
|
||||
*zr = zr2;
|
||||
|
||||
zr2 = (*zr*ocos(ax) + *yr*osin(ax));
|
||||
yr2 = (*zr*osin(ax) - *yr*ocos(ax));
|
||||
*zr = zr2;
|
||||
*yr = yr2;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
drawshdisk(int x0, int y0, int r)
|
||||
{
|
||||
int x = 0;
|
||||
int y;
|
||||
int ly; /* last y */
|
||||
int delta;
|
||||
int c; /* color at center */
|
||||
int d; /* delta */
|
||||
|
||||
#define SLIMIT 17
|
||||
#define SRANGE 15
|
||||
|
||||
if (r <= SLIMIT) {
|
||||
/* range checking is already (more or less) done... */
|
||||
(*gDrawStarFunc)(&gBuffer8[x0 + gWidth*y0], 10+r*5);
|
||||
//gBuffer8[x0+W*y0] = 10+r*5;
|
||||
return;
|
||||
}
|
||||
|
||||
if (r < SLIMIT+SRANGE)
|
||||
r = ((r-SLIMIT)*SLIMIT)/SRANGE+1;
|
||||
|
||||
y = ly = r; /* AAaargh */
|
||||
delta = 3-2*r;
|
||||
|
||||
do {
|
||||
if (y != ly) {
|
||||
/* dont overlap these lines */
|
||||
c = ((r-y+1)<<13)/r;
|
||||
d = -c/(x+1);
|
||||
|
||||
if (y == x+1) /* this would overlap with the next x lines */
|
||||
goto TOTO; /* WHY NOT */
|
||||
|
||||
/* note : for "normal" numbers (not too big) :
|
||||
(unsigned int)(x) < M <=> 0<=x<H
|
||||
(because if x<0, then (unsigned)(x) = 2**32-|x| which is
|
||||
BIG and thus >H )
|
||||
|
||||
This is clearly a stupid, unmaintanable, unreadable "optimization".
|
||||
But i like it :)
|
||||
*/
|
||||
if ((uint32)(y0-y-1) < gHeight-3)
|
||||
memshset(&gBuffer8[x0 + gWidth*(y0-y+1)] ,c,d, x);
|
||||
if ((uint32)(y0+y-1) < gHeight-3)
|
||||
memshset(&gBuffer8[x0 + gWidth*(y0+y)] ,c,d, x);
|
||||
}
|
||||
TOTO:
|
||||
c = ((r-x+1)<<13)/r;
|
||||
d = -c/(y);
|
||||
|
||||
if ((uint32)(y0-x-1) < gHeight-3)
|
||||
memshset(&gBuffer8[x0 + gWidth*(y0-x)] ,c,d, y);
|
||||
if ((uint32)(y0+x-1) < gHeight-3)
|
||||
memshset(&gBuffer8[x0 + gWidth*(y0+x+1)] ,c,d, y);
|
||||
|
||||
ly = y;
|
||||
if (delta < 0)
|
||||
delta += 4*x+6;
|
||||
else {
|
||||
delta += 4*(x-y)+10;
|
||||
y--;
|
||||
}
|
||||
x++;
|
||||
} while (x < y);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
drawGalaxy()
|
||||
{
|
||||
int r;
|
||||
int x,y;
|
||||
float rx,ry,rz;
|
||||
int i;
|
||||
float oa,ob,oc;
|
||||
float t;
|
||||
float a, b, c;
|
||||
matrix ma,mb,mc,mr;
|
||||
|
||||
/* t is the parametric coordinate for the animation;
|
||||
change the scale value to change the speed of anim
|
||||
(independant of processor speed)
|
||||
*/
|
||||
static bigtime_t firstTime = system_time();
|
||||
t = ((double)gSpeed * system_time()-firstTime)/1000000.0; //opti_scale_time(0.418, &demo_elapsed_time);
|
||||
|
||||
a = 0.9*t;
|
||||
b = t;
|
||||
c = 1.1*t;
|
||||
|
||||
setrmat(a,0,0,&ma);
|
||||
setrmat(0,b,0,&mb);
|
||||
mulmat(&ma,&mb,&mc);
|
||||
setrmat(0,0,c,&ma);
|
||||
mulmat(&ma,&mc,&mr);
|
||||
|
||||
oa = 140 * osin(a);
|
||||
ob = 140 * ocos(b);
|
||||
oc = 240 * osin(c);
|
||||
|
||||
if (gMotionBlur) {
|
||||
/* mblur does something like that:
|
||||
* (or did, perhaps it's another version!..)
|
||||
|
||||
for (i=0; i<W*H; i++)
|
||||
gBuffer8[i]= (gBuffer8[i]>>3) + (gBuffer8[i]>>1);
|
||||
*/
|
||||
mblur (gBuffer8, gWidth*gHeight);
|
||||
} else
|
||||
memset(gBuffer8,0,gWidth*gHeight);
|
||||
|
||||
for (i = 0; i < GMAX; i++) {
|
||||
rx = gal[i].x;
|
||||
ry = gal[i].y;
|
||||
rz = gal[i].z;
|
||||
|
||||
mulvec(&mr, &rx, &ry, &rz);
|
||||
|
||||
rx += oa;
|
||||
ry += ob;
|
||||
rz += oc;
|
||||
rz += 300;
|
||||
|
||||
if (rz > 5) {
|
||||
x = (int)(15*rx/(rz/5+1)) + gWidth/2; /* tain jcomprend plus rien */
|
||||
y = (int)(15*ry/(rz/5+1)) + gHeight/2; /* a ces formules de daube !! */
|
||||
r = (int)(3*gal[i].r / (rz/4+3))+2;
|
||||
|
||||
if (x > 5 && x < gWidth-6 && y > 5 && y < gHeight-6)
|
||||
// if ((uint32)x < gWidth-1 && (uint32)y < gHeight-1)
|
||||
drawshdisk(x,y,r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
setPalette()
|
||||
{
|
||||
int i;
|
||||
|
||||
switch (gPaletteScheme) {
|
||||
case 0: // yellow
|
||||
default:
|
||||
for (i = 0; i < 30; i++)
|
||||
gPalette[i] = (uint8)(i*8/10) << 16 | (uint8)(i*6/10) << 8; // | (uint8)(i*3/10);
|
||||
|
||||
for (i = 30; i < 256; i++) {
|
||||
uint8 r = (i);
|
||||
uint8 g = (i*i >> 8); //(i*8/10);
|
||||
uint8 b = i >= 240 ? (i-240) << 3 : 0; //(i*2/10);
|
||||
|
||||
gPalette[i] = ((r << 16) | (g << 8) | (b));
|
||||
}
|
||||
break;
|
||||
case 1: // blue
|
||||
for (i = 0; i < 30; i++)
|
||||
gPalette[i] = (uint8)(i*8/10); // << 16 | (uint8)(i*6/10) << 8; // | (uint8)(i*3/10);
|
||||
|
||||
for (i = 30; i < 256; i++) {
|
||||
uint8 b = (i);
|
||||
uint8 g = (i*i >> 8); //(i*8/10);
|
||||
uint8 r = i >= 240 ? (i-240) << 3 : 0; //(i*2/10);
|
||||
|
||||
gPalette[i] = ((r << 16) | (g << 8) | (b));
|
||||
}
|
||||
break;
|
||||
case 2: // red
|
||||
for (i = 0;i < 128;i++)
|
||||
gPalette[i] = (uint8)i << 16; // << 16 | (uint8)(i*6/10) << 8; // | (uint8)(i*3/10);
|
||||
|
||||
for (i = 128;i < 256;i++)
|
||||
{
|
||||
uint8 r = i;
|
||||
uint8 c = (uint8)((cos((i-256) / 42.0)*0.5 + 0.5)*225);
|
||||
|
||||
gPalette[i] = ((r << 16) | (c << 8) | c);
|
||||
}
|
||||
/* for (i = 192;i < 224;i++)
|
||||
{
|
||||
uint8 c = (i-192);
|
||||
gPalette[i] = gPalette[i] & 0xff0000 | c << 8 | c;
|
||||
}
|
||||
for (i = 224;i < 256;i++)
|
||||
{
|
||||
uint8 c = (i-224)/2;
|
||||
c = 32 + c*c*6/10;
|
||||
gPalette[i] = gPalette[i] & 0xff0000 | c << 8 | c;
|
||||
}
|
||||
*/ break;
|
||||
case 3: // green
|
||||
for (i = 0; i < 30; i++)
|
||||
gPalette[i] = (uint8)(i*8/10) << 8; // << 16 | (uint8)(i*6/10) << 8; // | (uint8)(i*3/10);
|
||||
|
||||
for (i = 30; i < 256; i++) {
|
||||
uint8 g = (i);
|
||||
uint8 r = (i*i >> 8); //(i*8/10);
|
||||
uint8 b = i >= 240 ? (i-240) << 3 : 0; //(i*2/10);
|
||||
|
||||
gPalette[i] = ((r << 16) | (g << 8) | (b));
|
||||
}
|
||||
break;
|
||||
case 4: // grey
|
||||
for (i = 0; i < 256; i++) {
|
||||
uint8 c = i*15/16 + 10;
|
||||
gPalette[i] = c << 16 | c << 8 | c;
|
||||
}
|
||||
break;
|
||||
case 5: // cold
|
||||
for (i = 0; i < 30; i++)
|
||||
gPalette[i] = (uint8)(i*8/10) << 16; // << 16 | (uint8)(i*6/10) << 8; // | (uint8)(i*3/10);
|
||||
|
||||
for (i = 30; i < 256; i++) {
|
||||
uint8 r = i;
|
||||
uint8 c = (uint8)((cos((i-255) / 82.0)*0.5 + 0.5)*255);
|
||||
|
||||
gPalette[i] = ((r << 16) | (c << 8) | c);
|
||||
}
|
||||
break;
|
||||
case 6: // original
|
||||
for (i = 0; i < 256; i++) {
|
||||
uint32 c = *(char *)&i;
|
||||
gPalette[i] = c << 16 | c << 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* for (i = 0;i < 256;i++)
|
||||
{
|
||||
uint8 r = (i);
|
||||
uint8 g = (i*i >> 8); //(i*8/10);
|
||||
uint8 b = 0; //(i*2/10);
|
||||
|
||||
gPalette[i] = ((r << 16) | (g << 8) | (b));
|
||||
}
|
||||
*/
|
||||
/* for (i = 240;i < 256;i++)
|
||||
gPalette[i] = (uint8)i << 16 | (uint8)i << 8 | (uint8)(i*6/10);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************/
|
||||
// #pragma mark -
|
||||
// #pragma mark SimpleSlider
|
||||
|
||||
class SimpleSlider : public BSlider {
|
||||
public:
|
||||
SimpleSlider(BRect frame, const char *label, BMessage *msg)
|
||||
: BSlider(frame, B_EMPTY_STRING, B_EMPTY_STRING, msg, 1, 100)
|
||||
{
|
||||
SetLimitLabels("1", "100");
|
||||
SetHashMarks(B_HASH_MARKS_BOTTOM);
|
||||
SetHashMarkCount(11);
|
||||
fLabel = label;
|
||||
};
|
||||
|
||||
char *UpdateText() const
|
||||
{
|
||||
sprintf(fText, "%s: %d", fLabel, Value());
|
||||
|
||||
return fText;
|
||||
};
|
||||
|
||||
private:
|
||||
mutable char fText[32];
|
||||
const char *fLabel;
|
||||
};
|
||||
|
||||
|
||||
/***********************************************************************************/
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
class SettingsView : public BView {
|
||||
public:
|
||||
SettingsView(BRect frame);
|
||||
|
||||
virtual void AttachedToWindow();
|
||||
virtual void MessageReceived(BMessage *msg);
|
||||
|
||||
private:
|
||||
BMenuField *fWidthMenu,*fColorMenu,*fBorderMenu;
|
||||
BCheckBox *fMotionCheck;
|
||||
BSlider *fSpeedSlider,*fFramesSlider;
|
||||
};
|
||||
|
||||
|
||||
SettingsView::SettingsView(BRect frame)
|
||||
: BView(frame,"",B_FOLLOW_NONE,B_WILL_DRAW)
|
||||
{
|
||||
MoveBy(0, -25); // the view is not where it should
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SettingsView::AttachedToWindow()
|
||||
{
|
||||
int32 height,y;
|
||||
{
|
||||
font_height fontHeight;
|
||||
GetFontHeight(&fontHeight);
|
||||
height = (int32)(fontHeight.ascent + fontHeight.descent + fontHeight.leading)+10;
|
||||
}
|
||||
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
||||
|
||||
BRect rect(10,0,0,25);
|
||||
rect.right = Bounds().right;
|
||||
|
||||
BStringView *string = new BStringView(rect,B_EMPTY_STRING,"Nebula");
|
||||
string->SetFontSize(18.0);
|
||||
AddChild(string);
|
||||
|
||||
rect.left += 60; rect.bottom -= 2;
|
||||
string = new BStringView(rect,B_EMPTY_STRING,VERSION_STRING);
|
||||
AddChild(string);
|
||||
|
||||
// rect.OffsetBy(0,height+5);
|
||||
rect.left = 10; rect.top = 27; rect.bottom = 17+height;
|
||||
string = new BStringView(rect,B_EMPTY_STRING,"© 2001-2004 pinc Software, Axel Dörfler.");
|
||||
string->SetAlignment(B_ALIGN_CENTER);
|
||||
AddChild(string);
|
||||
|
||||
BPopUpMenu *popMenu = new BPopUpMenu("");
|
||||
BMenuItem *item;
|
||||
|
||||
int32 widths[] = { 320, 512, 576, 640, 800, 1024, 1152, 1280, 1400, 1600 };
|
||||
for (int32 i = 0; i < sizeof(widths) / sizeof(widths[0]); i++) {
|
||||
BMessage *msg = new BMessage(kMsgWidth);
|
||||
char label[64];
|
||||
sprintf(label, "%ld pixel", widths[i]);
|
||||
msg->AddInt32("width", widths[i]);
|
||||
popMenu->AddItem(item = new BMenuItem(label, msg));
|
||||
|
||||
if (gSettingsWidth == widths[i])
|
||||
item->SetMarked(true);
|
||||
}
|
||||
|
||||
popMenu->SetTargetForItems(this);
|
||||
rect.left = 10; rect.OffsetBy(0,height);
|
||||
fWidthMenu = new BMenuField(rect, "res", "Internal Horizontal Resolution:", popMenu);
|
||||
fWidthMenu->SetDivider(155);
|
||||
AddChild(fWidthMenu);
|
||||
|
||||
popMenu = new BPopUpMenu("");
|
||||
|
||||
const char *colorSchemes[] = {"yellow","blue/cyan","red","green","grey","cold","orange (original)"};
|
||||
for (int i = 0; i < 7; i++) {
|
||||
BMessage *msg = new BMessage(kMsgColorScheme);
|
||||
msg->AddInt8("scheme",(int8)i);
|
||||
popMenu->AddItem(item = new BMenuItem(colorSchemes[i],msg));
|
||||
if (gPaletteScheme == i)
|
||||
item->SetMarked(true);
|
||||
}
|
||||
|
||||
popMenu->SetTargetForItems(this);
|
||||
rect.OffsetBy(0,height);
|
||||
fColorMenu = new BMenuField(rect,"col","Color Scheme:",popMenu);
|
||||
fColorMenu->SetDivider(110);
|
||||
AddChild(fColorMenu);
|
||||
|
||||
popMenu = new BPopUpMenu("");
|
||||
|
||||
const char *blankBorderFormats[] = {"fullscreen, no borders","16:9, wide-screen","2:3.5, cinemascope","only a slit"};
|
||||
for (int8 i = 0;i < 4;i++)
|
||||
{
|
||||
BMessage *msg = new BMessage(kMsgBlankBorders);
|
||||
msg->AddInt8("border",i);
|
||||
popMenu->AddItem(item = new BMenuItem(blankBorderFormats[i],msg));
|
||||
if (gBlankBorders == i)
|
||||
item->SetMarked(true);
|
||||
}
|
||||
|
||||
popMenu->SetTargetForItems(this);
|
||||
rect.OffsetBy(0,height);
|
||||
fBorderMenu = new BMenuField(rect,"cinema","Blank Border Format:",popMenu);
|
||||
fBorderMenu->SetDivider(110);
|
||||
AddChild(fBorderMenu);
|
||||
|
||||
rect.OffsetBy(0,height+3);
|
||||
fMotionCheck = new BCheckBox(rect,B_EMPTY_STRING,"Enable Motion Blur",new BMessage(kMsgMotionBlur));
|
||||
fMotionCheck->SetTarget(this);
|
||||
fMotionCheck->SetValue((int)gMotionBlur);
|
||||
AddChild(fMotionCheck);
|
||||
fMotionCheck->ResizeToPreferred();
|
||||
|
||||
rect.OffsetBy(0, height - 3); rect.bottom += 2*height; rect.right -= 10;
|
||||
fSpeedSlider = new SimpleSlider(rect,"Speed",new BMessage(kMsgSpeed));
|
||||
fSpeedSlider->SetValue((gSpeed - 0.002)/0.05);
|
||||
fSpeedSlider->SetTarget(this);
|
||||
AddChild(fSpeedSlider);
|
||||
fSpeedSlider->ResizeToPreferred();
|
||||
|
||||
rect.OffsetBy(0, 2 * height + 15);
|
||||
fFramesSlider = new SimpleSlider(rect,"Maximum Frames Per Second",new BMessage(kMsgFrames));
|
||||
fFramesSlider->SetValue(gMaxFramesPerSecond);
|
||||
fFramesSlider->SetTarget(this);
|
||||
AddChild(fFramesSlider);
|
||||
fFramesSlider->ResizeToPreferred();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SettingsView::MessageReceived(BMessage *msg)
|
||||
{
|
||||
switch(msg->what) {
|
||||
case kMsgWidth:
|
||||
if (msg->FindInt32("width",&gSettingsWidth) == B_OK) {
|
||||
if (gSettingsWidth > 1600)
|
||||
gSettingsWidth = 1600;
|
||||
else if (gSettingsWidth < 320)
|
||||
gSettingsWidth = 320;
|
||||
}
|
||||
break;
|
||||
case kMsgColorScheme:
|
||||
if (msg->FindInt8("scheme",&gPaletteScheme) == B_OK)
|
||||
setPalette();
|
||||
break;
|
||||
case kMsgBlankBorders:
|
||||
msg->FindInt8("border",&gBlankBorders);
|
||||
break;
|
||||
case kMsgMotionBlur:
|
||||
gMotionBlur = fMotionCheck->Value() > 0;
|
||||
break;
|
||||
case kMsgSpeed:
|
||||
gSpeed = 0.002 + 0.05 * fSpeedSlider->Value();
|
||||
//printf("value = %d, gSpeed = %f\n",fSpeedSlider->Value(),gSpeed);
|
||||
break;
|
||||
case kMsgFrames:
|
||||
gMaxFramesPerSecond = fFramesSlider->Value();
|
||||
gScreenSaver->SetTickSize((bigtime_t)(1000000LL / gMaxFramesPerSecond));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************************************************************/
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
class Nebula : public BScreenSaver {
|
||||
public:
|
||||
Nebula(BMessage *message, image_id id);
|
||||
|
||||
virtual void StartConfig(BView *view);
|
||||
virtual status_t SaveState(BMessage *state) const;
|
||||
|
||||
virtual status_t StartSaver(BView *view, bool preview);
|
||||
virtual void StopSaver();
|
||||
virtual void Draw(BView *view, int32 frame);
|
||||
|
||||
private:
|
||||
float fFactor;
|
||||
bool fStarted;
|
||||
};
|
||||
|
||||
|
||||
Nebula::Nebula(BMessage *message, image_id id)
|
||||
: BScreenSaver(message, id)
|
||||
{
|
||||
message->FindFloat("speed", 0, &gSpeed);
|
||||
message->FindInt32("width", 0, &gSettingsWidth);
|
||||
message->FindBool("motionblur", 0, &gMotionBlur);
|
||||
message->FindFloat("max_fps", 0, &gMaxFramesPerSecond);
|
||||
message->FindInt8("scheme", 0, &gPaletteScheme);
|
||||
message->FindInt8("border", 0, &gBlankBorders);
|
||||
|
||||
if (gSpeed < 0.01f)
|
||||
gSpeed = 0.4f;
|
||||
if (gSettingsWidth < 320)
|
||||
gSettingsWidth = 320;
|
||||
if (gMaxFramesPerSecond < 1.f)
|
||||
gMaxFramesPerSecond = 40.0f;
|
||||
|
||||
gScreenSaver = this;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Nebula::StartConfig(BView *view)
|
||||
{
|
||||
view->AddChild(new SettingsView(view->Frame()));
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Nebula::SaveState(BMessage *state) const
|
||||
{
|
||||
state->AddFloat("speed", gSpeed);
|
||||
state->AddInt32("width", gSettingsWidth);
|
||||
state->AddBool("motionblur", gMotionBlur);
|
||||
state->AddFloat("max_fps", gMaxFramesPerSecond);
|
||||
state->AddInt8("scheme", gPaletteScheme);
|
||||
state->AddInt8("border", gBlankBorders);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Nebula::StartSaver(BView *view, bool preview)
|
||||
{
|
||||
// initialize palette
|
||||
setPalette();
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 512; i++) {
|
||||
precos[i]=cos(i*M_PI/256);
|
||||
presin[i]=sin(i*M_PI/256);
|
||||
}
|
||||
|
||||
// uniforme cubique
|
||||
/* for (i = 0;i < GMAX;i++)
|
||||
{
|
||||
gal[i].x = 1*((rand()&1023) - 512);
|
||||
gal[i].y = 1*((rand()&1023) - 512);
|
||||
gal[i].z = 1*((rand()&1023) - 512);
|
||||
gal[i].r = rand()&63;
|
||||
}
|
||||
*/
|
||||
|
||||
for (i = 0; i < GMAX; i++) {
|
||||
float r, th, h, dth;
|
||||
|
||||
r = rand()*1.0 / RAND_MAX;
|
||||
r = (1-r)*(1-r)+0.05;
|
||||
|
||||
if (r < 0.12)
|
||||
th = rand()*M_PI*2/RAND_MAX;
|
||||
else {
|
||||
th = (rand()&3)*M_PI_2 + r*r*2;
|
||||
dth = rand()*1.0/RAND_MAX;
|
||||
dth = dth*dth*2;
|
||||
th+=dth;
|
||||
}
|
||||
gal[i].x = (int)(512*r*cos(th));
|
||||
gal[i].z = (int)(512*r*sin(th));
|
||||
h = (1+cos(r*M_PI))*150;
|
||||
dth = rand()*1.0/RAND_MAX;
|
||||
gal[i].y = (int)(h*(dth-0.5));
|
||||
gal[i].r = (int)((2-r)*60 + 31);
|
||||
}
|
||||
gal[0].x = gal[0].y = gal[0].z = 0;
|
||||
gal[0].r = 320;
|
||||
|
||||
if (preview)
|
||||
gWidth = 320;
|
||||
else
|
||||
gWidth = gSettingsWidth;
|
||||
|
||||
fFactor = (view->Bounds().Width()+1) / gWidth;
|
||||
if ((int)fFactor != fFactor)
|
||||
fFactor += 0.01;
|
||||
|
||||
// 4:3
|
||||
gHeight = (int32)((view->Bounds().Height()+1)/fFactor + 0.5f);
|
||||
// calculate blank border format (if not in preview)
|
||||
if (!preview) switch (gBlankBorders) {
|
||||
case 1: // 16:9
|
||||
gHeight = (int32)(gHeight * 0.703125 + 0.5);
|
||||
break;
|
||||
case 2: // 2:3.5
|
||||
gHeight = (int32)(gHeight * 0.534 + 0.5);
|
||||
break;
|
||||
case 3:
|
||||
gHeight /= 5;
|
||||
break;
|
||||
}
|
||||
view->SetScale(fFactor);
|
||||
|
||||
switch (gWidth) {
|
||||
default:
|
||||
case 320: gDrawStarFunc = draw_stars320; break;
|
||||
case 512: gDrawStarFunc = draw_stars512; break;
|
||||
case 576: gDrawStarFunc = draw_stars576; break;
|
||||
case 640: gDrawStarFunc = draw_stars640; break;
|
||||
case 800: gDrawStarFunc = draw_stars800; break;
|
||||
case 1024: gDrawStarFunc = draw_stars1024; break;
|
||||
case 1152: gDrawStarFunc = draw_stars1152; break;
|
||||
case 1280: gDrawStarFunc = draw_stars1280; break;
|
||||
case 1400: gDrawStarFunc = draw_stars1400; break;
|
||||
case 1600: gDrawStarFunc = draw_stars1600; break;
|
||||
}
|
||||
|
||||
gBitmap = new BBitmap(BRect(0, 0, gWidth - 1, gHeight - 1), B_RGB32);
|
||||
gBuffer8 = (char *)malloc(gWidth * gHeight);
|
||||
|
||||
SetTickSize((bigtime_t)(1000000LL / gMaxFramesPerSecond));
|
||||
fStarted = true;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Nebula::StopSaver()
|
||||
{
|
||||
free(gBuffer8);
|
||||
gBuffer8 = NULL;
|
||||
|
||||
delete gBitmap;
|
||||
gBitmap = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Nebula::Draw(BView *view, int32)
|
||||
{
|
||||
if (fStarted) {
|
||||
view->SetHighColor(0, 0, 0, 0);
|
||||
view->FillRect(view->Frame());
|
||||
view->MovePenTo(0, (view->Bounds().Height() / fFactor - 1 - gHeight) / 2);
|
||||
|
||||
fStarted = false;
|
||||
}
|
||||
uint32 *buffer32 = (uint32 *)gBitmap->Bits();
|
||||
|
||||
drawGalaxy();
|
||||
|
||||
for (int x = 0, end = gWidth * gHeight; x < end; x++)
|
||||
buffer32[x] = gPalette[(uint8)gBuffer8[x]];
|
||||
|
||||
view->DrawBitmap(gBitmap);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************/
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
extern "C" _EXPORT BScreenSaver *
|
||||
instantiate_screen_saver(BMessage *message, image_id image)
|
||||
{
|
||||
return new Nebula(message, image);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
resource app_version {
|
||||
major = 1,
|
||||
middle = 2,
|
||||
minor = 0,
|
||||
|
||||
variety = B_APPV_FINAL,
|
||||
internal = 0,
|
||||
|
||||
short_info = "Nebula2",
|
||||
long_info = "Copyright ©2001-2004 pinc Software."
|
||||
};
|
||||
|
||||
resource large_icon array {
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFF00000000000000000000FFFFFFFFFFFFFFFF00FFFFFFFFFFFF"
|
||||
$"FFFFFFFF0000001B3F3F3F3F3F3F3F3F3F0000FFFFFFFFFF00FFFF000000FFFF"
|
||||
$"FFFFFF001B3F1B3F3F1B3F1C1B161919193F3F0000FFFF000000FFFFFF00FFFF"
|
||||
$"FFFF003F3F1B1C1B3F3F3F3F1C1B1C161619193F1C0000FFFFFFFFFF00FFFFFF"
|
||||
$"FFFF003F153F3F1B3F1B1C1C3F3F1C161616161919193F00FFFFFF000000FFFF"
|
||||
$"FFFF003F3F3F3F3F3F1B1C3F1C1C3F3F1C1B1C161619193F00FFFFFFFFFFFFFF"
|
||||
$"FFFF003F3F3F3F3F3F3F3F3F1C1B1C1C3F3F3F1B1C16163F3F00FFFFFFFFFFFF"
|
||||
$"FF003F3F3F3F153F3F3F3F3F3F1B1C3F1C1C3F3F3F16163F3F3F00FFFFFFFFFF"
|
||||
$"FF003F3F3F3F1515153F3F3F3F3F3F3F1C3F1C1C1B1C3F3F3F3F3F00FFFFFFFF"
|
||||
$"FF003F3F3F1517171715153F3F3F3F3F3F3F3F3F1C1C3F1C3F3F3F00FFFFFFFF"
|
||||
$"FF003F3F3F15171717171715153F3F3F3F3F3F3F3F3F1C3F3F3F3F3F00FFFFFF"
|
||||
$"FF003F3F3F1517173F3F17171715153F3F3F3F3F3F3F3F3F3F0E3F3F00FFFFFF"
|
||||
$"003F3F3F3F15173F3F3F3F3F3F171715153F3F3F3F3F3F3F0E0F3F3F00FFFFFF"
|
||||
$"003F3F3F3F17173F3F3F3F3F3F3F3F171715153F3F3F3F3F0F0E3F3F00FFFFFF"
|
||||
$"003F0000000000003F3F3F3F3F3F3F3F1717173F3F3F113F3F0F0E3F3F00FFFF"
|
||||
$"000000150A0A1C000000003F3F3F3F3F3F3F3F113F3F113F3F0F0F3F3F00FFFF"
|
||||
$"FFFF00150A0A1C0000000000003F3F3F3F3F3F113F3F113F3F3F3F3F3F0000FF"
|
||||
$"FFFF00150A0A1C00000000000000003F3F3F3F113F3F113F3F3F3F00000000FF"
|
||||
$"FFFF00150A0B1C000000000000000000003F113F3F3F113F3F3F0000FFFFFFFF"
|
||||
$"FFFF001500001B1B1C000000000000000000123F3F3F113F3F000000FFFFFFFF"
|
||||
$"FFFF0000151515151B1C1C00000000000000123F3F3F113F3F000F00FFFFFFFF"
|
||||
$"FFFFFF000015151515151C1C1C000000000000003F3F3F11000F0F001111FFFF"
|
||||
$"FFFFFFFFFF0000151515151C1C1C1B000000000000003F3F000F0F00111111FF"
|
||||
$"FFFFFFFFFFFFFF0000151515151B1C1C1B0000001B1500000F0F0F0011111111"
|
||||
$"FFFFFFFFFFFFFFFFFF0000151515151C1B1C1C001B16150F0F0E0F0011111111"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFF0000151615151C1B1C1C15150F0F0F0F0011111111"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFF0000151615151C1B16150F0F0F0E00111111FF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00001515151615150F0F0F00111111FFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000151515160E0F00111111FFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000016150F00111111FFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000111111FFFFFFFFFF"
|
||||
};
|
||||
|
||||
resource mini_icon array {
|
||||
$"FFFFFFFF0404040404FFFFFF00FFFFFF"
|
||||
$"FFFF00003F3F3F3F3F00FFFF00FF00FF"
|
||||
$"FF003F1C3F3F1C1C16190A00FFFF00FF"
|
||||
$"FF003F3F3F1C1C3F1C1C161900FFFFFF"
|
||||
$"FF043F153F3F3F1C1C3F3F163F00FFFF"
|
||||
$"043F3F1717153F3F3F3F1C3F3F0AFFFF"
|
||||
$"043F3F173F1717153F3F3F3F3F3F00FF"
|
||||
$"003F3F173F3F17173F153F3F0F3F00FF"
|
||||
$"00000A0A00000017173F3F113F0F04FF"
|
||||
$"FF000A1C00000000173F3F113F3F00FF"
|
||||
$"FF00001C1C00000000123F113F00FFFF"
|
||||
$"FF000016161C000000003F3F000611FF"
|
||||
$"FFFFFF000016161C1B000A000F061111"
|
||||
$"FFFFFFFFFF000016151B1C150F061111"
|
||||
$"FFFFFFFFFFFFFF06001516150F0011FF"
|
||||
$"FFFFFFFFFFFFFFFFFF0600060011FFFF"
|
||||
};
|
||||
@@ -0,0 +1,136 @@
|
||||
|
||||
.text
|
||||
|
||||
/* memshset (char *dst, int center_shade,int fixed_shade, int length_2) */
|
||||
/* dst is the *center* of the dst segment */
|
||||
/* center shade is the initial color (at center) (!! 8:8 also) */
|
||||
/* fixed_shade is the fixed point increment (8:8) */
|
||||
/* length_2 is the 1/2 length to set (to the left, and to the right) */
|
||||
|
||||
/* a memaddshadedset function (or something like that ) */
|
||||
|
||||
.globl memshset
|
||||
.type memshset,@function
|
||||
|
||||
|
||||
memshset:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
|
||||
pushl %edi /* registres a sauvegarder (convention gcc) */
|
||||
pushl %esi
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%edi /* dst */
|
||||
movl 12(%ebp),%eax
|
||||
movl 16(%ebp),%ebx /* fixed point */
|
||||
movl 20(%ebp),%ecx /* half length */
|
||||
|
||||
orl %ecx,%ecx
|
||||
jz 2f
|
||||
|
||||
movl %edi,%esi
|
||||
subl %ecx,%edi /* %edi left segment */
|
||||
addl %ecx,%esi /* %esi right segment */
|
||||
incl %esi /* dont overlap at middle */
|
||||
|
||||
movl %ecx,%edx
|
||||
negl %edx
|
||||
decl %edx /* for in-advance incl in loop */
|
||||
1:
|
||||
addl %ebx,%eax /* increment color */
|
||||
incl %edx
|
||||
|
||||
addb %ah,(%edi,%ecx) /* increment left pixel */
|
||||
jc 3f /* did overflow ? */
|
||||
|
||||
addb %ah,(%esi,%edx) /* increment right pixel */
|
||||
jc 4f
|
||||
|
||||
decl %ecx
|
||||
jnz 1b
|
||||
|
||||
2:
|
||||
popl %ebx
|
||||
popl %esi
|
||||
popl %edi
|
||||
|
||||
leave
|
||||
ret
|
||||
/* function goes on (several exit points): */
|
||||
3:
|
||||
movb $255,(%edi,%ecx)
|
||||
addb %ah,(%esi,%edx) /* we recopy all the code.. to avoid jmps (hmm some asm guru here : is it really efficient ?.. */
|
||||
jc 4f
|
||||
decl %ecx
|
||||
jnz 1b
|
||||
|
||||
popl %ebx
|
||||
popl %esi
|
||||
popl %edi
|
||||
|
||||
leave
|
||||
ret
|
||||
|
||||
4:
|
||||
movb $255,(%esi,%edx)
|
||||
decl %ecx
|
||||
jnz 1b
|
||||
|
||||
popl %ebx
|
||||
popl %esi
|
||||
popl %edi
|
||||
|
||||
leave
|
||||
ret
|
||||
/* function really stops here */
|
||||
|
||||
|
||||
/*********************************** motion blur **********************************/
|
||||
|
||||
/* do the "motion blur" (actually the pixel fading) */
|
||||
.globl mblur
|
||||
.type mblur,@function
|
||||
|
||||
mblur:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
|
||||
pushl %edi
|
||||
pushl %esi
|
||||
pushl %ebx
|
||||
|
||||
movl 8(%ebp),%edi /* dst */
|
||||
movl 12(%ebp),%ecx /* number */
|
||||
movl $0x7f7f7f7f,%edx /* clear 1 upper bits */
|
||||
movl $0x1f1f1f1f,%esi /* clear 3 upper bits */
|
||||
shrl $2,%ecx
|
||||
jz 2f
|
||||
|
||||
1:
|
||||
movl (%edi),%eax
|
||||
addl $4,%edi
|
||||
|
||||
movl %eax,%ebx
|
||||
shrl $1,%eax
|
||||
|
||||
|
||||
shrl $3,%ebx
|
||||
andl %edx,%eax
|
||||
|
||||
andl %esi,%ebx
|
||||
addl %ebx,%eax
|
||||
|
||||
decl %ecx
|
||||
movl %eax,-4(%edi)
|
||||
|
||||
|
||||
jnz 1b
|
||||
|
||||
|
||||
2:
|
||||
popl %ebx
|
||||
popl %esi
|
||||
popl %edi
|
||||
leave
|
||||
ret
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
.text
|
||||
|
||||
/* this function needs a definition from outside (the width of the bitmap) */
|
||||
|
||||
// #define STAR_WIDTH 320
|
||||
// #define STAR_FUNCTION draw_stars320
|
||||
|
||||
/* draw a star (5 pixels) */
|
||||
.globl STAR_FUNCTION
|
||||
.type STAR_FUNCTION,@function
|
||||
|
||||
STAR_FUNCTION:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
|
||||
pushl %edi
|
||||
|
||||
movl 8(%ebp),%edi /* dst */
|
||||
movl 12(%ebp),%edx /* inc */
|
||||
|
||||
addb %dl,(STAR_WIDTH)(%edi)
|
||||
jnc 3f
|
||||
movb $255,(STAR_WIDTH)(%edi)
|
||||
|
||||
3:
|
||||
shrb $1,%dl
|
||||
movb (%edi),%al
|
||||
movb (STAR_WIDTH-1)(%edi),%cl
|
||||
addb %dl,%al
|
||||
jc 1f
|
||||
addb %dl,%cl
|
||||
jc 2f
|
||||
movb %al,(%edi)
|
||||
movb %cl,(STAR_WIDTH-1)(%edi)
|
||||
jmp 31f
|
||||
1:
|
||||
addb %dl,%cl
|
||||
jc 2f
|
||||
movb $255,(%edi)
|
||||
movb %cl,(STAR_WIDTH-1)(%edi)
|
||||
|
||||
jmp 31f
|
||||
|
||||
2:
|
||||
movb $255,(%edi)
|
||||
movb $255,(STAR_WIDTH-1)(%edi)
|
||||
|
||||
|
||||
31:
|
||||
movb 2*STAR_WIDTH(%edi),%al
|
||||
movb (STAR_WIDTH+1)(%edi),%cl
|
||||
addb %dl,%al
|
||||
jc 11f
|
||||
addb %dl,%cl
|
||||
jc 21f
|
||||
movb %al,2*STAR_WIDTH(%edi)
|
||||
movb %cl,(STAR_WIDTH+1)(%edi)
|
||||
popl %edi
|
||||
leave
|
||||
ret
|
||||
|
||||
|
||||
11:
|
||||
addb %dl,%cl
|
||||
jc 21f
|
||||
movb $255,(2*STAR_WIDTH)(%edi)
|
||||
movb %cl,(STAR_WIDTH+1)(%edi)
|
||||
popl %edi
|
||||
leave
|
||||
ret
|
||||
|
||||
21:
|
||||
movb $255,2*STAR_WIDTH(%edi)
|
||||
movb $255,(STAR_WIDTH+1)(%edi)
|
||||
popl %edi
|
||||
leave
|
||||
ret
|
||||
@@ -0,0 +1,48 @@
|
||||
#include $(TOP)/config/make.cfg
|
||||
|
||||
CFLAGS = -O3 -ffast-math -funroll-loops -fomit-frame-pointer -fexpensive-optimizations
|
||||
#CFLAGS = -g
|
||||
|
||||
LIBS = -lbe -lscreensaver
|
||||
NAME = Nebula
|
||||
FILES = Nebula
|
||||
ASMFILES = draw
|
||||
STARFILE = draw_stars
|
||||
STARWIDTHS = 320 512 576 640 800 1024 1152 1280 1400 1600
|
||||
OBJDIR = obj.x86
|
||||
|
||||
STARFILES := $(foreach f, $(STARWIDTHS), $(STARFILE)$(f))
|
||||
|
||||
SRCS := $(foreach f, $(FILES), $(f).cpp)
|
||||
ASMSRCS := $(foreach f, $(ASMFILES), $(f).S)
|
||||
|
||||
OBJS := $(foreach f, $(FILES) $(ASMFILES) $(STARFILES), $(OBJDIR)/$(f).o)
|
||||
|
||||
all: $(NAME)
|
||||
|
||||
install: all
|
||||
@mv $(NAME) /boot/home/config/add-ons/Screen\ Savers/
|
||||
|
||||
test: all
|
||||
@screen_blanker /boot/home/develop/div/ScreenSavers/$(NAME)/$(NAME)
|
||||
|
||||
clean:
|
||||
@rm $(OBJDIR)/*
|
||||
|
||||
Nebula: $(OBJS)
|
||||
@echo "Linking $@..."
|
||||
@$(CC) $(OBJS) -o $@ -nostart $(LIBS)
|
||||
xres -o $@ $@.rsrc
|
||||
mimeset -f $@
|
||||
|
||||
$(OBJDIR)/%.o : %.cpp
|
||||
$(CC) -c $*.cpp -o $(OBJDIR)/$*.o $(CFLAGS)
|
||||
|
||||
$(OBJDIR)/$(STARFILE)%.o : $(STARFILE).S
|
||||
$(CC) -c $(STARFILE).S -DSTAR_WIDTH=$* -DSTAR_FUNCTION=draw_stars$* -o $@
|
||||
|
||||
$(OBJDIR)/%.o : %.S
|
||||
$(CC) -c $*.S -o $(OBJDIR)/$*.o $(CFLAGS)
|
||||
|
||||
tar zip backup:
|
||||
@zip `basename $(NAME)`-`date +%Y-%m-%d`.zip *.[chS]* *.rsrc makefile
|
||||
Reference in New Issue
Block a user