Пример #1
0
// In build mode, find the block at the given screen position. Return the chunk and the
// data about it to the pointers.
chunk *gameDialog::FindSelectedSurface(int x, int y, ChunkOffsetCoord *coc, int *surfaceDir) {
	ChunkShaderPicking *pickShader = ChunkShaderPicking::Make();
	pickShader->EnableProgram();
	pickShader->View(gViewMatrix);
	pickShader->Projection(gProjectionMatrix);

	glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Use black sky for picking
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	DrawLandscape(0, DL_Picking);
	pickShader->DisableProgram();

	unsigned char pixel[4];
	glReadPixels(x,gViewport[3] - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel[0]);

	if (pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0)
		return 0; // Click on the sky, which is drawn black. No block will have this value (facing is 1-6).

	PickingData coding;
	coding.rgb[0] = pixel[0]; coding.rgb[1] = pixel[1]; coding.rgb[2] = pixel[2]; // Compensate for transformation in shader.
#if 0
	gMsgWindow.Add("Pick (%d,%d,%d) facing %d, d(%d,%d,%d)", coding.bitmap.x, coding.bitmap.y, coding.bitmap.z,
	               coding.bitmap.facing, coding.bitmap.dx-1, coding.bitmap.dy-1, coding.bitmap.dz-1);
	gMsgWindow.Add("RGB: %d,%d,%d", coding.rgb[0], coding.rgb[1], coding.rgb[2]);
#endif

	ChunkCoord cc;

	gPlayer.GetChunkCoord(&cc);
	cc.x += coding.bitmap.dx-1;
	cc.y += coding.bitmap.dy-1;
	cc.z += coding.bitmap.dz-1;
	chunk *cp = ChunkFind(&cc, false);
	coc->x = coding.bitmap.x; coc->y = coding.bitmap.y; coc->z = coding.bitmap.z;

	if (surfaceDir)
		*surfaceDir = coding.bitmap.facing;

	checkError("gameDialog::FindSelectedSurface");
	return cp;
}
Пример #2
0
Файл: wav.c Проект: Tilka/vlc
/*****************************************************************************
 * Open: check file and initializes structures
 *****************************************************************************/
static int Open( vlc_object_t * p_this )
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;

    const uint8_t *p_peek;
    unsigned int   i_size;
    unsigned int   i_extended;
    const char    *psz_name;

    WAVEFORMATEXTENSIBLE *p_wf_ext = NULL;
    WAVEFORMATEX         *p_wf = NULL;

    /* Is it a wav file ? */
    if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 )
        return VLC_EGENERIC;

    if( memcmp( p_peek, "RIFF", 4 ) || memcmp( &p_peek[8], "WAVE", 4 ) )
    {
        return VLC_EGENERIC;
    }

    p_demux->pf_demux     = Demux;
    p_demux->pf_control   = Control;
    p_demux->p_sys        = p_sys = malloc( sizeof( *p_sys ) );
    if( unlikely(!p_sys) )
        return VLC_ENOMEM;

    p_sys->p_es           = NULL;
    p_sys->i_chans_to_reorder = 0;
    p_sys->i_channel_mask = 0;

    /* skip riff header */
    if( stream_Read( p_demux->s, NULL, 12 ) != 12 )
        goto error;

    /* search fmt chunk */
    if( ChunkFind( p_demux, "fmt ", &i_size ) )
    {
        msg_Err( p_demux, "cannot find 'fmt ' chunk" );
        goto error;
    }
    i_size += 2;
    if( i_size < sizeof( WAVEFORMATEX ) )
    {
        msg_Err( p_demux, "invalid 'fmt ' chunk" );
        goto error;
    }
    if( stream_Read( p_demux->s, NULL, 8 ) != 8 )
        goto error;


    /* load waveformatex */
    p_wf_ext = malloc( i_size );
    if( unlikely( !p_wf_ext ) )
        goto error;

    p_wf         = &p_wf_ext->Format;
    p_wf->cbSize = 0;
    i_size      -= 2;
    if( stream_Read( p_demux->s, p_wf, i_size ) != (int)i_size ||
            ( ( i_size & 1 ) && stream_Read( p_demux->s, NULL, 1 ) != 1 ) )
    {
        msg_Err( p_demux, "cannot load 'fmt ' chunk" );
        goto error;
    }

    es_format_Init( &p_sys->fmt, AUDIO_ES, 0 );
    wf_tag_to_fourcc( GetWLE( &p_wf->wFormatTag ), &p_sys->fmt.i_codec,
                      &psz_name );
    p_sys->fmt.audio.i_channels      = GetWLE ( &p_wf->nChannels );
    p_sys->fmt.audio.i_rate          = GetDWLE( &p_wf->nSamplesPerSec );
    p_sys->fmt.audio.i_blockalign    = GetWLE( &p_wf->nBlockAlign );
    p_sys->fmt.i_bitrate             = GetDWLE( &p_wf->nAvgBytesPerSec ) * 8;
    p_sys->fmt.audio.i_bitspersample = GetWLE( &p_wf->wBitsPerSample );
    if( i_size >= sizeof(WAVEFORMATEX) )
        p_sys->fmt.i_extra = __MIN( GetWLE( &p_wf->cbSize ), i_size - sizeof(WAVEFORMATEX) );
    i_extended = 0;

    /* Handle new WAVE_FORMAT_EXTENSIBLE wav files */
    /* see the following link for more information:
     * http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EFAA */
    if( GetWLE( &p_wf->wFormatTag ) == WAVE_FORMAT_EXTENSIBLE &&
            i_size >= sizeof( WAVEFORMATEXTENSIBLE ) &&
            ( p_sys->fmt.i_extra + sizeof( WAVEFORMATEX )
              >= sizeof( WAVEFORMATEXTENSIBLE ) ) )
    {
        unsigned i_channel_mask;
        GUID guid_subformat;

        guid_subformat = p_wf_ext->SubFormat;
        guid_subformat.Data1 = GetDWLE( &p_wf_ext->SubFormat.Data1 );
        guid_subformat.Data2 = GetWLE( &p_wf_ext->SubFormat.Data2 );
        guid_subformat.Data3 = GetWLE( &p_wf_ext->SubFormat.Data3 );

        sf_tag_to_fourcc( &guid_subformat, &p_sys->fmt.i_codec, &psz_name );

        i_extended = sizeof( WAVEFORMATEXTENSIBLE ) - sizeof( WAVEFORMATEX );
        p_sys->fmt.i_extra -= i_extended;

        i_channel_mask = GetDWLE( &p_wf_ext->dwChannelMask );
        if( i_channel_mask )
        {
            int i_match = 0;
            for( unsigned i = 0; i < sizeof(pi_channels_src)/sizeof(*pi_channels_src); i++ )
            {
                if( i_channel_mask & pi_channels_src[i] )
                {
                    if( !( p_sys->i_channel_mask & pi_channels_in[i] ) )
                        i_match++;

                    i_channel_mask &= ~pi_channels_src[i];
                    p_sys->i_channel_mask |= pi_channels_in[i];

                    if( i_match >= p_sys->fmt.audio.i_channels )
                        break;
                }
            }
            if( i_channel_mask )
                msg_Warn( p_demux, "Some channels are unrecognized or uselessly specified (0x%x)", i_channel_mask );
            if( i_match < p_sys->fmt.audio.i_channels )
            {
                int i_missing = p_sys->fmt.audio.i_channels - i_match;
                msg_Warn( p_demux, "Trying to fill up unspecified position for %d channels", p_sys->fmt.audio.i_channels - i_match );

                static const uint32_t pi_pair[] = { AOUT_CHAN_REARLEFT|AOUT_CHAN_REARRIGHT,
                                                    AOUT_CHAN_MIDDLELEFT|AOUT_CHAN_MIDDLERIGHT,
                                                    AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT
                                                  };
                /* FIXME: Unused yet
                static const uint32_t pi_center[] = { AOUT_CHAN_REARCENTER,
                                                      0,
                                                      AOUT_CHAN_CENTER }; */

                /* Try to complete with pair */
                for( unsigned i = 0; i < sizeof(pi_pair)/sizeof(*pi_pair); i++ )
                {
                    if( i_missing >= 2 && !(p_sys->i_channel_mask & pi_pair[i] ) )
                    {
                        i_missing -= 2;
                        p_sys->i_channel_mask |= pi_pair[i];
                    }
                }
                /* Well fill up with what we can */
                for( unsigned i = 0; i < sizeof(pi_channels_in)/sizeof(*pi_channels_in) && i_missing > 0; i++ )
                {
                    if( !( p_sys->i_channel_mask & pi_channels_in[i] ) )
                    {
                        p_sys->i_channel_mask |= pi_channels_in[i];
                        i_missing--;

                        if( i_missing <= 0 )
                            break;
                    }
                }

                i_match = p_sys->fmt.audio.i_channels - i_missing;
            }
            if( i_match < p_sys->fmt.audio.i_channels )
            {
                msg_Err( p_demux, "Invalid/unsupported channel mask" );
                p_sys->i_channel_mask = 0;
            }
        }
    }
    else if( GetWLE( &p_wf->wFormatTag ) == WAVE_FORMAT_PCM &&
             p_sys->fmt.audio.i_channels > 2 && p_sys->fmt.audio.i_channels <= 9 )
    {
        for( int i = 0; i < p_sys->fmt.audio.i_channels; i++ )
            p_sys->i_channel_mask |= pi_channels_in[i];
    }

    if( p_sys->i_channel_mask )
    {
        if( p_sys->fmt.i_codec == VLC_FOURCC('a','r','a','w') ||
                p_sys->fmt.i_codec == VLC_FOURCC('p','c','m',' ') ||
                p_sys->fmt.i_codec == VLC_FOURCC('a','f','l','t') )
            p_sys->i_chans_to_reorder =
                aout_CheckChannelReorder( pi_channels_in, NULL,
                                          p_sys->i_channel_mask,
                                          p_sys->pi_chan_table );

        msg_Dbg( p_demux, "channel mask: %x, reordering: %u",
                 p_sys->i_channel_mask, p_sys->i_chans_to_reorder );
    }

    p_sys->fmt.audio.i_physical_channels =
        p_sys->fmt.audio.i_original_channels = p_sys->i_channel_mask;

    if( p_sys->fmt.i_extra > 0 )
    {
        p_sys->fmt.p_extra = malloc( p_sys->fmt.i_extra );
        if( unlikely(!p_sys->fmt.p_extra) )
        {
            p_sys->fmt.i_extra = 0;
            goto error;
        }
        memcpy( p_sys->fmt.p_extra, (uint8_t *)p_wf + sizeof( WAVEFORMATEX ) + i_extended,
                p_sys->fmt.i_extra );
    }

    msg_Dbg( p_demux, "format: 0x%4.4x, fourcc: %4.4s, channels: %d, "
             "freq: %u Hz, bitrate: %uKo/s, blockalign: %d, bits/samples: %d, "
             "extra size: %d",
             GetWLE( &p_wf->wFormatTag ), (char *)&p_sys->fmt.i_codec,
             p_sys->fmt.audio.i_channels, p_sys->fmt.audio.i_rate,
             p_sys->fmt.i_bitrate / 8 / 1024, p_sys->fmt.audio.i_blockalign,
             p_sys->fmt.audio.i_bitspersample, p_sys->fmt.i_extra );

    free( p_wf );
    p_wf = NULL;

    switch( p_sys->fmt.i_codec )
    {
    case VLC_FOURCC( 'a', 'r', 'a', 'w' ):
    case VLC_FOURCC( 'a', 'f', 'l', 't' ):
    case VLC_FOURCC( 'u', 'l', 'a', 'w' ):
    case VLC_CODEC_ALAW:
    case VLC_CODEC_MULAW:
    case VLC_FOURCC( 'p', 'c', 'm', ' ' ):
        if( FrameInfo_PCM( &p_sys->i_frame_size, &p_sys->i_frame_samples,
                           &p_sys->fmt ) )
            goto error;
        break;
    case VLC_CODEC_ADPCM_MS:
        if( FrameInfo_MS_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples,
                                &p_sys->fmt ) )
            goto error;
        break;
    case VLC_CODEC_ADPCM_IMA_WAV:
        if( FrameInfo_IMA_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples,
                                 &p_sys->fmt ) )
            goto error;
        break;
    case VLC_FOURCC( 'm', 's', 0x00, 0x61 ):
    case VLC_FOURCC( 'm', 's', 0x00, 0x62 ):
        /* FIXME not sure at all FIXME */
        if( FrameInfo_MS_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples,
                                &p_sys->fmt ) )
            goto error;
        break;
    case VLC_CODEC_MPGA:
    case VLC_CODEC_A52:
        /* FIXME set end of area FIXME */
        goto error;
    case VLC_CODEC_GSM_MS:
    case VLC_CODEC_ADPCM_G726:
    case VLC_CODEC_TRUESPEECH:
    case VLC_CODEC_ATRAC3:
    case VLC_CODEC_G723_1:
        if( FrameInfo_MSGSM( &p_sys->i_frame_size, &p_sys->i_frame_samples,
                             &p_sys->fmt ) )
            goto error;
        break;
    default:
        msg_Err( p_demux, "unsupported codec (%4.4s)",
                 (char*)&p_sys->fmt.i_codec );
        goto error;
    }

    if( p_sys->i_frame_size <= 0 || p_sys->i_frame_samples <= 0 )
    {
        msg_Dbg( p_demux, "invalid frame size: %i %i", p_sys->i_frame_size,
                 p_sys->i_frame_samples );
        goto error;
    }
    if( p_sys->fmt.audio.i_rate <= 0 )
    {
        msg_Dbg( p_demux, "invalid sample rate: %i", p_sys->fmt.audio.i_rate );
        goto error;
    }

    msg_Dbg( p_demux, "found %s audio format", psz_name );

    if( ChunkFind( p_demux, "data", &p_sys->i_data_size ) )
    {
        msg_Err( p_demux, "cannot find 'data' chunk" );
        goto error;
    }
    if( stream_Read( p_demux->s, NULL, 8 ) != 8 )
        goto error;
    p_sys->i_data_pos = stream_Tell( p_demux->s );

    if( p_sys->fmt.i_bitrate <= 0 )
    {
        p_sys->fmt.i_bitrate = (int64_t)p_sys->i_frame_size *
                               p_sys->fmt.audio.i_rate * 8 / p_sys->i_frame_samples;
    }

    p_sys->p_es = es_out_Add( p_demux->out, &p_sys->fmt );

    date_Init( &p_sys->pts, p_sys->fmt.audio.i_rate, 1 );
    date_Set( &p_sys->pts, 1 );

    return VLC_SUCCESS;

error:
    msg_Err( p_demux, "An error occurred during wav demuxing" );
    free( p_wf );
    free( p_sys );
    return VLC_EGENERIC;
}
Пример #3
0
void gameDialog::render() {
	static bool first = true; // Only true first time function is called
	// Clear list of special effects. It will be added again automatically every frame
	gShadows.Clear();
	gFogs.Clear();
	gPlayer.UpdatePositionSmooth();

	// Can't select objects that are dead
	if (fSelectedObject && (fSelectedObject->IsDead() || !fSelectedObject->InGame()))
		ClearSelection();
	glm::vec3 playerOffset = gPlayer.GetOffsetToChunk();

	gViewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -fCameraDistance));
	gViewMatrix = glm::rotate(gViewMatrix, _angleVert, glm::vec3(1.0f, 0.0f, 0.0f));
	gViewMatrix = glm::rotate(gViewMatrix, _angleHor, glm::vec3(0.0f, 1.0f, 0.0f));
	gViewMatrix = glm::translate(gViewMatrix, -playerOffset);

	glm::mat4 T1 = glm::translate(glm::mat4(1), playerOffset);
	glm::mat4 R1 = glm::rotate(glm::mat4(1), _angleVert, glm::vec3(-1.0f, 0.0f, 0.0f));
	glm::mat4 R2 = glm::rotate(glm::mat4(1), _angleHor, glm::vec3(0.0f, -1.0f, 0.0f));
	glm::mat4 T2 = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, fCameraDistance));
	glm::vec4 camera = T1 * R2 * R1 * T2 * glm::vec4(0,0,0,1);
	gUniformBuffer.Camera(camera);

	gDrawnQuads = 0;
	gNumDraw = 0;

	gUniformBuffer.Update();

	if (first) {
		gBillboard.InitializeTextures(fShader);
	}

	fRenderControl->Draw(fSelectedObject, fUnderWater, this->ThirdPersonView(), fSelectedObject, fDrawMap && !gDebugOpenGL, fMapWidth);

	//=========================================================================
	// Various effects drawn after the deferred shader
	//=========================================================================

	if (!fDrawMap) {
		this->DrawPlayerStats();
		if (fShowWeapon && gMode.Get() != GameMode::CONSTRUCT && !fShowInventory && !this->ThirdPersonView())
			this->DrawWeapon();
		static bool wasDead = false;
		if (gPlayer.IsDead() && !wasDead) {
			wasDead = true;
			fShowMainDialog = true;
			fHideDialog = false;
			dialog::DispatchDraw(fDrawTexture, 1.0f, new MessageDialog("Oops", "You are dead.\n\nYou will be revived, and transported back to your starting place.\n\nThe place can be changed with a scroll of resurrection point.", revive));
			this->ClearForDialog();
		} else if (fShowInventory)
			gInventory.DrawInventory(fDrawTexture);
		else if (fShowMainDialog)
			dialog::DispatchDraw(fDrawTexture, fHideDialog ? 0.5f : 1.0f);
		else if (sgPopup.length() > 0) {
			// There are some messages that shall be shown in a popup dialog.
			fShowMainDialog = true;
			fHideDialog = false;
			dialog::DispatchDraw(fDrawTexture, 1.0f, new MessageDialog(sgPopupTitle, sgPopup, 0));
			sgPopupTitle = "Ephenation"; // Reset to default.
			sgPopup.clear();
			this->ClearForDialog();
		}

		if (!gPlayer.IsDead())
			wasDead = false;
		DrawCompassRose();
	}

	if (fDrawMap && gDebugOpenGL) {
		// Very inefficient algorithm, computing the map every frame.
		std::unique_ptr<Map> map(new Map);
		float alpha = 1.0f;
		if (!sShowAlternateBitmap)
			sTextureIterator = gDebugTextures.begin();
		glBindTexture(GL_TEXTURE_2D, *sTextureIterator); // Override
		map->Draw(alpha);
	}

	if (fSelectedObject) {
		fSelectedObject->RenderHealthBar(fHealthBar, _angleHor);
	}
	gOtherPlayers.RenderPlayerStats(fHealthBar, _angleHor);
	bool newHealing = false;
	if (gPlayer.fFlags & UserFlagHealed) {
		newHealing = true;
		gPlayer.fFlags &= ~UserFlagHealed;
	}
	this->DrawHealingAnimation(newHealing);

	char buff[1000]; // TODO: Ugly way to create dynamic strings
	static double slAverageFps = 0.0;
	double tm = gCurrentFrameTime;
	static double prevTime = 0.0;
	double deltaTime = tm - prevTime;
	// Use a decay filter on the FPS
	slAverageFps = 0.97*slAverageFps + 0.03/deltaTime;
	prevTime = tm;

	if (gMode.Get() == GameMode::TELEPORT) {
		TeleportClick(fHealthBar, _angleHor, renderViewAngle, 0, 0, false);
	}

	//=========================================================================
	// Various text messages
	//=========================================================================
	{
		// A big chunk with text drawing. But first some background to improve contrast.
		int yOffset = Options::fgOptions.fFontSize*2;
		float totalWinHeight = gViewport[3]; // Measured in pixels
		glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(-1.0f, 1.0f - yOffset*2/totalWinHeight, 0.0f));
		float yScale = yOffset*2/totalWinHeight;
		model = glm::scale(model, glm::vec3(2.0f, yScale, 1.0f));
		this->fHealthBar->DrawSquare(model, 0.0f, 0.0f, 0.0f, 0.3f); // Draw a black square, almost transparent, as a background

		gDrawFont.Enable();
		gDrawFont.UpdateProjection();
		sprintf(buff, "Level %ld, Hp %d%%, Exp %d%%", gPlayer.fLevel, (int)(gPlayer.fHp * 100), (int)(gPlayer.fExp * 100));
		gDrawFont.vsfl.prepareSentence(fPlayerStatusSentence, buff);
		int w = int(strlen(buff)*Options::fgOptions.fFontSize*0.8f); // An approximation
		gDrawFont.SetOffset(gViewport[2]-w, 0);
		gDrawFont.vsfl.renderSentence(fPlayerStatusSentence);

		if (gCurrentPing == 0.0)
			sprintf(buff, "Triangles %7d, draw calls %d. Fps %03d", gDrawnQuads, gNumDraw, int(slAverageFps));
		else
			sprintf(buff, "Triangles %7d, draw calls %d. Fps %03d, ping %.1f ms", gDrawnQuads, gNumDraw, int(slAverageFps), gCurrentPing*1000.0);
		gDrawFont.SetOffset(0,0);
		gDrawFont.vsfl.prepareSentence(fFPSsentence, buff);
		gDrawFont.vsfl.renderSentence(fFPSsentence);
		if (gMode.Get() == GameMode::CONSTRUCT) {
			ChunkCoord cc;
			gPlayer.GetChunkCoord(&cc);
			const chunk *cp = ChunkFind(&cc, false);
			unsigned int uid = 1000000;
			shared_ptr<ChunkBlocks> cb;
			if (cp)
				cb = cp->fChunkBlocks;
			if (cb)
				uid = cb->fOwner;
			gDrawFont.SetOffset(0,float(yOffset));
			sprintf(buff, "Construction mode, Chunk (%d,%d,%d) offset: %.1f,%.1f,%.1f, coord(%.1f,%.1f,%.1f) owner %d",
			        cc.x, cc.y, cc.z, playerOffset.x, playerOffset.y, playerOffset.z, gPlayer.x/100.0, gPlayer.y/100.0, gPlayer.z/100.0, uid);
			gDrawFont.vsfl.renderAndDiscard(buff);
		}

		if (fEnterDebugText) {
			int bottomLine = gViewport[3]-25;
			gDrawFont.SetOffset(0, float(bottomLine));
			gDrawFont.vsfl.renderSentence(fInputPromptSentence);
		}
		gDrawFont.Disable();
		gScrollingMessages.Update();
	}

	if (!fDrawMap && !fShowInventory && (!fShowMainDialog || fHideDialog)) {
		if (gShowMsgWindow)
			gMsgWindow.Render();

		if (gMode.Get() == GameMode::CONSTRUCT)
			fBuildingBlocks->Draw(gProjectionMatrix);
	}

	if (gDebugOpenGL) {
		checkError("gameDialog::render debug", false);
		static double prevPrint = 0.0;
		if (gCurrentFrameTime > prevPrint + 5.0) {
			if (gToggleTesting)
				WorstTime::Report();
			else
				TimeMeasure::Report();
			prevPrint = gCurrentFrameTime;
		}
	}
	chunk::DegradeBusyList_gl();
	first = false;
}
Пример #4
0
void gameDialog::HandleKeyPress(int key, int action) {
	if (fShowMainDialog && !fHideDialog && key != GLFW_KEY_ESC) {
		if (action == GLFW_PRESS) {
			dialog::DispatchKey(key);
		}
		return;
	}
	if (key >= GLFW_KEY_F1 && key <= GLFW_KEY_F25) {
		gInventory.UseObjectFunctionKey(key);
		return;
	}
	if (fEnterDebugText) {
		// Override the switch, and catch all characters.
		switch (key) {
		case GLFW_KEY_KP_ENTER:
		case GLFW_KEY_ENTER: // ENTER key
			// Done.
			if (fDebugTextLength > 0) {
				fDebugText[fDebugTextLength+3] = 0;
				const char *begin = (char *)fDebugText+3;
				const char *end = strchr(begin, ' ');
				if (end == 0)
					prevCommand = string(begin, fDebugTextLength);
				else
					prevCommand = string(begin, end-begin+1); // Including the space character
				// printf("Prev command: '%s'\n", prevCommand.c_str());
				// There is something to send. Make a proper command of it.
				fDebugText[0] = fDebugTextLength+3;
				fDebugText[1] = 0;
				fDebugText[2] = CMD_DEBUG;
				SendMsg(fDebugText, fDebugTextLength+3);
			}
			// Fall through!
		case GLFW_KEY_ESC: // Escape key, stop composing a command.
			glfwDisable(GLFW_KEY_REPEAT);
			fEnterDebugText = false;
			break;
		case GLFW_KEY_BACKSPACE: // Back space
			if (fDebugTextLength > 0) {
				if (strncmp((const char *)fDebugText+3, prevCommand.c_str(), fDebugTextLength) == 0)
					fDebugTextLength = 1; // Keep the leading '/' only
				else
					fDebugTextLength--;
			}
			this->HandleCharacter(0,0); // Update the shown string
			break;
		}
		return;
	}

	int x, y;
	glfwGetMousePos(&x, &y);
	switch (key) {
	case GLFW_KEY_KP_0:
		// if (gPlayer.fAdmin > 0)
		gToggleTesting = !gToggleTesting;
		break;
	case GLFW_KEY_ESC:
		if (fDrawMap)
			fDrawMap = false;
		else if (fShowInventory) {
			fShowInventory = false;
			gMsgWindow.SetAlternatePosition(0,0,false);
		} else {
			if (fShowMainDialog) {
				fHideDialog = !fHideDialog;
			}
			fShowMainDialog = true;
			this->ClearForDialog();
		}
		break;
	case 'C':
		if (gMode.Get() == GameMode::CONSTRUCT) { // Toggle construction mode
			gMode.Set(GameMode::GAME);
			gShowFramework = false;
		} else {
			gMode.Set(GameMode::CONSTRUCT);
		}
		if (gPlayer.fKnownPosition) {
			ChunkCoord player_cc;
			// Force the current chunk to be redrawn, adapted for construction mode
			gPlayer.GetChunkCoord(&player_cc);
			for (int dx=-1; dx<2; dx++) for (int dy=-1; dy<2; dy++) for (int dz=-1; dz < 2; dz++) {
						ChunkCoord cc = player_cc;
						cc.x += dx; cc.y += dy; cc.z += dz;
						chunk *cp = ChunkFind(&cc, true);
						cp->SetDirty(true);
					}
		}
		break;
	case GLFW_KEY_TAB:
		if (fDrawMap && gDebugOpenGL) {
			// A debug feature to iterate through various interesting bitmaps
			if (!sShowAlternateBitmap) {
				sShowAlternateBitmap = true;
				sTextureIterator = gDebugTextures.begin();
			} else
				sTextureIterator++;
			if (sTextureIterator == gDebugTextures.end()) {
				sShowAlternateBitmap = false;
			}
			break;
		}
		// Find next monster after the selected one.
		fSelectedObject = gMonsters.GetNext(fSelectedObject);
		break;
	case GLFW_KEY_LALT: // ALT key
		if (gMode.Get() == GameMode::CONSTRUCT) {
			gShowFramework = true; // Only when in construction mode
		} else if (gMode.Get() == GameMode::GAME && gPlayer.fAdmin > 0) {
			gMode.Set(GameMode::TELEPORT);
			gAdminTP = true;
		}
		break;
	case '1': // Autoattack
		if (!fSelectedObject) {
			// Find next monster after the selected one.
			fSelectedObject = gMonsters.GetNext(fSelectedObject);
		}
		if (fSelectedObject) { // Initiate attack on a monster, if any.
			unsigned char b[] = { 7, 0, CMD_ATTACK_MONSTER, 0, 0, 0, 0 };
			EncodeUint32(b+3, fSelectedObject->GetId());
			SendMsg(b, sizeof b);
		}
		break;
	case '2': { // Heal self
		unsigned char b[] = { 0x04, 0x00, CMD_PLAYER_ACTION, UserActionHeal };
		SendMsg(b, sizeof b);
		break;
	}
	case '3': { // Instant extra attack
		unsigned char b[] = { 0x04, 0x00, CMD_PLAYER_ACTION, UserActionCombAttack };
		SendMsg(b, sizeof b);
		break;
	}
	case 'I': {
		// Toggle inventory screen
		fShowInventory = !fShowInventory;
		if (!fShowInventory)
			gMsgWindow.SetAlternatePosition(0,0,false);
		break;
	}
	case 'T':
		dumpGraphicsMemoryStats();
		gShowMsgWindow = !gShowMsgWindow;
		break;
	case GLFW_KEY_KP_ENTER:
	case GLFW_KEY_ENTER: // ENTER key
		fEnterDebugText = true;
		glfwEnable(GLFW_KEY_REPEAT);
		// gDebugWindow.Add("Starting to capture debug message");
		strcpy((char *)fDebugText+3, prevCommand.c_str());
		fDebugTextLength = prevCommand.length();
		fDebugText[fDebugTextLength+3] = 0;
		this->HandleCharacter(0,0); // Update the shown string
		break;
	case GLFW_KEY_SPACE: { // space key
		unsigned char b[] = { 0x03, 0x00, CMD_JUMP };
		SendMsg(b, sizeof b);
		gSoundControl.RequestSound(SoundControl::SPlayerJump);
		break;
	}
	case 'W':
	case GLFW_KEY_UP:
		if (!fMovingFwd) {
			fMovingFwd = true;
			unsigned char b[] = { 0x03, 0x00, 10 };
			SendMsg(b, sizeof b);
			// printf("Start fwd\n");
		}
		break;
	case 'A':
	case GLFW_KEY_LEFT:
		if (!fMovingLeft) {
			fMovingLeft = true;
			unsigned char b[] = { 0x03, 0x00, 14 };
			SendMsg(b, sizeof b);
			// printf("Start left\n");
		}
		break;
	case 'S':
	case GLFW_KEY_DOWN:
		if (!fMovingBwd) {
			fMovingBwd = true;
			unsigned char b[] = { 0x03, 0x00, 12 };
			SendMsg(b, sizeof b);
			// printf("Start bwd\n");
		}
		break;
	case 'D':
	case GLFW_KEY_RIGHT:
		if (!fMovingRight) {
			fMovingRight = true;
			unsigned char b[] = { 0x03, 0x00, 16 };
			SendMsg(b, sizeof b);
			// printf("Start right\n");
		}
		break;
	case GLFW_KEY_INSERT: // Insert, closer to delete on a full keyboard
	case 'B':
		if (gMode.Get() == GameMode::CONSTRUCT) {
			this->AttachBlockToSurface(x, y);
		}

		break;
	case GLFW_KEY_DEL: // Delete
	case 'V': // Closer to B when building
		if (gMode.Get() == GameMode::CONSTRUCT) {
			ClickOnBlock(x, y);
		}
		break;
	case GLFW_KEY_KP_SUBTRACT:
		switch(fCalibrationMode) {
		case CALIB_AMBIENT:
			Options::fgOptions.fAmbientLight -= 1;
			gMsgWindow.Add("Ambient light: %f", Options::fgOptions.fAmbientLight/100.0);
			break;
		case CALIB_EXPOSURE:
			Options::fgOptions.fExposure /= 1.1f;
			gMsgWindow.Add("Exposure: %f", Options::fgOptions.fExposure);
			break;
		case CALIB_WHITE_POINT:
			Options::fgOptions.fWhitePoint /= 1.1f;
			gMsgWindow.Add("White point: %f", Options::fgOptions.fWhitePoint);
			break;
		case CALIB_NONE:
			break;
		}
		break;
	case '-': {
		maxRenderDistance -= 5.0;
		if (maxRenderDistance < 5.0f && gDebugOpenGL)
			maxRenderDistance = 5.0f;
		if (maxRenderDistance < 40.0f && !gDebugOpenGL)
			maxRenderDistance = 40.0f;
		handleResize(gViewport[2], gViewport[3]);
		gMsgWindow.Add("Viewing distance: %f m", maxRenderDistance/2);
		break;
	}
	case GLFW_KEY_KP_ADD:
		switch(fCalibrationMode) {
		case CALIB_AMBIENT:
			Options::fgOptions.fAmbientLight += 1;
			gMsgWindow.Add("Ambient light: %f", Options::fgOptions.fAmbientLight/100.0);
			break;
		case CALIB_EXPOSURE:
			Options::fgOptions.fExposure *= 1.1f;
			gMsgWindow.Add("Exposure: %f", Options::fgOptions.fExposure);
			break;
		case CALIB_WHITE_POINT:
			Options::fgOptions.fWhitePoint *= 1.1f;
			gMsgWindow.Add("White point: %f", Options::fgOptions.fWhitePoint);
			break;
		case CALIB_NONE:
			break;
		}
		break;
	case '+': {
		// #255 long distances are not handled very well by neither server nor client
		maxRenderDistance += 5.0;
		if (maxRenderDistance > MAXRENDERDISTANCE)
			maxRenderDistance = MAXRENDERDISTANCE;
		handleResize(gViewport[2], gViewport[3]);
		gMsgWindow.Add("Viewing distance: %f m", maxRenderDistance/2);
		break;
	}
	case 'M': {
		fDrawMap = !fDrawMap;
		break;
	}
	case '\'': {
		if (gPlayer.fAdmin > 0)
			fUsingTorch = !fUsingTorch;
	}
	default:
		gMsgWindow.Add("Unknown key '%d'", key);
		break;
	}
	gGameDialog.UpdateRunningStatus(false);
}